|
|
@ -473,6 +473,8 @@ export default { |
|
|
waypointDragging: null, |
|
|
waypointDragging: null, |
|
|
waypointDragPending: null, |
|
|
waypointDragPending: null, |
|
|
WAYPOINT_DRAG_THRESHOLD_PX: 8, |
|
|
WAYPOINT_DRAG_THRESHOLD_PX: 8, |
|
|
|
|
|
/** 虚拟点(entry/exit)拖拽时的实时预览:{ routeId, dbId, position },拖拽时用 position 作为航点中心重算弧线 */ |
|
|
|
|
|
waypointDragPreview: null, |
|
|
/** 拖拽航点前相机 enableInputs 状态,松开时恢复 */ |
|
|
/** 拖拽航点前相机 enableInputs 状态,松开时恢复 */ |
|
|
waypointDragCameraInputsEnabled: undefined, |
|
|
waypointDragCameraInputsEnabled: undefined, |
|
|
lastClickWasDrag: false, |
|
|
lastClickWasDrag: false, |
|
|
@ -1840,6 +1842,7 @@ export default { |
|
|
//正式航线渲染函数 |
|
|
//正式航线渲染函数 |
|
|
renderRouteWaypoints(waypoints, routeId = 'default', platformId, platform, style) { |
|
|
renderRouteWaypoints(waypoints, routeId = 'default', platformId, platform, style) { |
|
|
if (!waypoints || waypoints.length < 1) return; |
|
|
if (!waypoints || waypoints.length < 1) return; |
|
|
|
|
|
this.waypointDragPreview = null; |
|
|
// 清理旧线 |
|
|
// 清理旧线 |
|
|
const lineId = `route-line-${routeId}`; |
|
|
const lineId = `route-line-${routeId}`; |
|
|
const existingLine = this.viewer.entities.getById(lineId); |
|
|
const existingLine = this.viewer.entities.getById(lineId); |
|
|
@ -1909,11 +1912,12 @@ export default { |
|
|
}); |
|
|
}); |
|
|
if (!this._routeWaypointIdsByRoute) this._routeWaypointIdsByRoute = {}; |
|
|
if (!this._routeWaypointIdsByRoute) this._routeWaypointIdsByRoute = {}; |
|
|
this._routeWaypointIdsByRoute[routeId] = waypoints.map((wp) => wp.id); |
|
|
this._routeWaypointIdsByRoute[routeId] = waypoints.map((wp) => wp.id); |
|
|
// 判断航点 i 是否为“转弯半径”航点(将用弧线两端两个点替代中心点) |
|
|
// 判断航点 i 是否为“转弯半径”航点(将用弧线两端两个点替代中心点);非首尾默认 45° 坡度 |
|
|
const isTurnWaypointWithArc = (i) => { |
|
|
const isTurnWaypointWithArc = (i) => { |
|
|
if (i < 1 || i >= waypoints.length - 1) return false; |
|
|
if (i < 1 || i >= waypoints.length - 1) return false; |
|
|
const wp = waypoints[i]; |
|
|
const wp = waypoints[i]; |
|
|
if (this.getWaypointRadius(wp) <= 0) return false; |
|
|
const effectiveAngle = this.getEffectiveTurnAngle(wp, i, waypoints.length); |
|
|
|
|
|
if (this.getWaypointRadius({ ...wp, turnAngle: effectiveAngle }) <= 0) return false; |
|
|
const nextPos = originalPositions[i + 1]; |
|
|
const nextPos = originalPositions[i + 1]; |
|
|
let nextLogical = nextPos; |
|
|
let nextLogical = nextPos; |
|
|
if (this.isHoldWaypoint(waypoints[i + 1])) { |
|
|
if (this.isHoldWaypoint(waypoints[i + 1])) { |
|
|
@ -1924,7 +1928,7 @@ export default { |
|
|
} |
|
|
} |
|
|
return !!nextLogical; |
|
|
return !!nextLogical; |
|
|
}; |
|
|
}; |
|
|
// 遍历并绘制航点标记:转弯半径处也画中心点+标签(与普通航点一致);盘旋处在圆心画点+标签 |
|
|
// 遍历并绘制航点标记:盘旋处在圆心画点+标签;有转弯半径时只画 entry/exit 两个虚拟点(见下方连线逻辑),不画原航点中心 |
|
|
waypoints.forEach((wp, index) => { |
|
|
waypoints.forEach((wp, index) => { |
|
|
const pos = originalPositions[index]; |
|
|
const pos = originalPositions[index]; |
|
|
if (this.isHoldWaypoint(wp)) { |
|
|
if (this.isHoldWaypoint(wp)) { |
|
|
@ -1956,6 +1960,8 @@ export default { |
|
|
}); |
|
|
}); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
// 有转弯半径的航点不显示中心点,只显示下方连线逻辑里添加的 entry/exit 两个虚拟点,点击虚拟点弹窗仍控制原航点(dbId) |
|
|
|
|
|
if (isTurnWaypointWithArc(index)) return; |
|
|
this.viewer.entities.add({ |
|
|
this.viewer.entities.add({ |
|
|
id: `wp_${routeId}_${wp.id}`, |
|
|
id: `wp_${routeId}_${wp.id}`, |
|
|
name: wp.name || `WP${index + 1}`, |
|
|
name: wp.name || `WP${index + 1}`, |
|
|
@ -2250,7 +2256,8 @@ export default { |
|
|
}); |
|
|
}); |
|
|
lastPos = exit; |
|
|
lastPos = exit; |
|
|
} else { |
|
|
} else { |
|
|
const radius = this.getWaypointRadius(wp); |
|
|
const effectiveAngle = this.getEffectiveTurnAngle(wp, i, waypoints.length); |
|
|
|
|
|
const radius = this.getWaypointRadius({ ...wp, turnAngle: effectiveAngle }); |
|
|
let nextLogical = nextPos; |
|
|
let nextLogical = nextPos; |
|
|
if (nextPos && i + 1 < waypoints.length && this.isHoldWaypoint(waypoints[i + 1])) { |
|
|
if (nextPos && i + 1 < waypoints.length && this.isHoldWaypoint(waypoints[i + 1])) { |
|
|
const holdParams = this.parseHoldParams(waypoints[i + 1]); |
|
|
const holdParams = this.parseHoldParams(waypoints[i + 1]); |
|
|
@ -2259,22 +2266,42 @@ export default { |
|
|
: this.getEllipseEntryPoint(originalPositions[i + 1], currPos, holdParams.semiMajor ?? 500, holdParams.semiMinor ?? 300, ((holdParams.headingDeg || 0) * Math.PI) / 180); |
|
|
: this.getEllipseEntryPoint(originalPositions[i + 1], currPos, holdParams.semiMajor ?? 500, holdParams.semiMinor ?? 300, ((holdParams.headingDeg || 0) * Math.PI) / 180); |
|
|
} |
|
|
} |
|
|
if (i < waypoints.length - 1 && radius > 0 && nextLogical) { |
|
|
if (i < waypoints.length - 1 && radius > 0 && nextLogical) { |
|
|
const arcPoints = this.computeArcPositions(lastPos, currPos, nextLogical, radius); |
|
|
const lastPosCloned = Cesium.Cartesian3.clone(lastPos); |
|
|
|
|
|
const currPosCloned = Cesium.Cartesian3.clone(currPos); |
|
|
|
|
|
const nextLogicalCloned = Cesium.Cartesian3.clone(nextLogical); |
|
|
|
|
|
const routeIdCloned = routeId; |
|
|
|
|
|
const dbIdCloned = wp.id; |
|
|
|
|
|
const that = this; |
|
|
|
|
|
const getArcPoints = () => { |
|
|
|
|
|
const center = (that.waypointDragPreview && that.waypointDragPreview.routeId === routeIdCloned && that.waypointDragPreview.dbId === dbIdCloned) |
|
|
|
|
|
? that.waypointDragPreview.position : currPosCloned; |
|
|
|
|
|
return that.computeArcPositions(lastPosCloned, center, nextLogicalCloned, radius); |
|
|
|
|
|
}; |
|
|
|
|
|
// 弧线用 CallbackProperty,拖拽虚拟点时实时重算实现动态预览 |
|
|
this.viewer.entities.add({ |
|
|
this.viewer.entities.add({ |
|
|
id: `arc-line-${routeId}-${i}`, |
|
|
id: `arc-line-${routeId}-${i}`, |
|
|
polyline: { positions: arcPoints, width: lineWidth, material: lineMaterial, arcType: Cesium.ArcType.NONE, zIndex: 20 }, |
|
|
show: false, |
|
|
|
|
|
polyline: { |
|
|
|
|
|
positions: new Cesium.CallbackProperty(getArcPoints, false), |
|
|
|
|
|
width: lineWidth, |
|
|
|
|
|
material: lineMaterial, |
|
|
|
|
|
arcType: Cesium.ArcType.NONE, |
|
|
|
|
|
zIndex: 20 |
|
|
|
|
|
}, |
|
|
properties: { routeId: routeId } |
|
|
properties: { routeId: routeId } |
|
|
}); |
|
|
}); |
|
|
// 转弯半径两侧航点与航线整体航点风格一致,点击均打开原航点编辑(dbId 为当前 wp.id) |
|
|
// 转弯半径两侧虚拟点也用 CallbackProperty,拖拽时随弧线实时更新 |
|
|
const wpName = wp.name || `WP${i + 1}`; |
|
|
const wpName = wp.name || `WP${i + 1}`; |
|
|
const arcEntry = arcPoints[0]; |
|
|
[0, 1].forEach((idx) => { |
|
|
const arcExit = arcPoints[arcPoints.length - 1]; |
|
|
|
|
|
[arcEntry, arcExit].forEach((pos, idx) => { |
|
|
|
|
|
const suffix = idx === 0 ? '_entry' : '_exit'; |
|
|
const suffix = idx === 0 ? '_entry' : '_exit'; |
|
|
|
|
|
const getPos = () => { |
|
|
|
|
|
const pts = getArcPoints(); |
|
|
|
|
|
return idx === 0 ? pts[0] : pts[pts.length - 1]; |
|
|
|
|
|
}; |
|
|
this.viewer.entities.add({ |
|
|
this.viewer.entities.add({ |
|
|
id: `wp_${routeId}_${wp.id}${suffix}`, |
|
|
id: `wp_${routeId}_${wp.id}${suffix}`, |
|
|
name: wpName, |
|
|
name: wpName, |
|
|
position: pos, |
|
|
position: new Cesium.CallbackProperty(getPos, false), |
|
|
properties: { |
|
|
properties: { |
|
|
isMissionWaypoint: true, |
|
|
isMissionWaypoint: true, |
|
|
routeId: routeId, |
|
|
routeId: routeId, |
|
|
@ -2298,6 +2325,7 @@ export default { |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
const arcPoints = getArcPoints(); |
|
|
finalPathPositions.push(...arcPoints); |
|
|
finalPathPositions.push(...arcPoints); |
|
|
lastPos = arcPoints[arcPoints.length - 1]; |
|
|
lastPos = arcPoints[arcPoints.length - 1]; |
|
|
} else { |
|
|
} else { |
|
|
@ -2326,14 +2354,14 @@ export default { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
/** 从各航点/弧线/盘旋实体取当前位置,供主航线折线实时连线(拖拽时动态跟随) */ |
|
|
/** 从各航点/弧线/盘旋实体取当前位置,供主航线折线实时连线(拖拽时动态跟随)。转弯处优先用弧线,不经过航点中心,只展示转弯半径 */ |
|
|
getRouteLinePositionsFromWaypointEntities(routeId) { |
|
|
getRouteLinePositionsFromWaypointEntities(routeId) { |
|
|
const ids = this._routeWaypointIdsByRoute && this._routeWaypointIdsByRoute[routeId]; |
|
|
const ids = this._routeWaypointIdsByRoute && this._routeWaypointIdsByRoute[routeId]; |
|
|
if (!ids || !ids.length || !this.viewer) return null; |
|
|
if (!ids || !ids.length || !this.viewer) return null; |
|
|
const now = Cesium.JulianDate.now(); |
|
|
const now = Cesium.JulianDate.now(); |
|
|
const positions = []; |
|
|
const positions = []; |
|
|
for (let i = 0; i < ids.length; i++) { |
|
|
for (let i = 0; i < ids.length; i++) { |
|
|
// 盘旋段优先用整圆弧线,不取圆心点,避免主折线出现穿过圆心的直线 |
|
|
// 盘旋段优先用整圆弧线,不取圆心点 |
|
|
const holdEnt = this.viewer.entities.getById(`hold-line-${routeId}-${i}`); |
|
|
const holdEnt = this.viewer.entities.getById(`hold-line-${routeId}-${i}`); |
|
|
if (holdEnt && holdEnt.polyline && holdEnt.polyline.positions) { |
|
|
if (holdEnt && holdEnt.polyline && holdEnt.polyline.positions) { |
|
|
const arr = holdEnt.polyline.positions.getValue(now); |
|
|
const arr = holdEnt.polyline.positions.getValue(now); |
|
|
@ -2342,14 +2370,7 @@ export default { |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
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}`); |
|
|
const arcEnt = this.viewer.entities.getById(`arc-line-${routeId}-${i}`); |
|
|
if (arcEnt && arcEnt.polyline && arcEnt.polyline.positions) { |
|
|
if (arcEnt && arcEnt.polyline && arcEnt.polyline.positions) { |
|
|
const arr = arcEnt.polyline.positions.getValue(now); |
|
|
const arr = arcEnt.polyline.positions.getValue(now); |
|
|
@ -2358,10 +2379,23 @@ export default { |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
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; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
return positions.length > 0 ? positions : null; |
|
|
return positions.length > 0 ? positions : null; |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** 渲染/路径用:非首尾航点默认转弯坡度 45°,首尾为 0 */ |
|
|
|
|
|
getEffectiveTurnAngle(wp, index, waypointsLength) { |
|
|
|
|
|
if (index === 0 || index === waypointsLength - 1) return wp.turnAngle != null ? wp.turnAngle : 0; |
|
|
|
|
|
return wp.turnAngle != null ? wp.turnAngle : 45; |
|
|
|
|
|
}, |
|
|
// 计算单个点的转弯半径 |
|
|
// 计算单个点的转弯半径 |
|
|
getWaypointRadius(wp) { |
|
|
getWaypointRadius(wp) { |
|
|
const speed = wp.speed || 800; |
|
|
const speed = wp.speed || 800; |
|
|
@ -2733,7 +2767,8 @@ export default { |
|
|
? this.getCircleEntryPoint(originalPositions[i + 1], currPos, holdParams.radius) |
|
|
? this.getCircleEntryPoint(originalPositions[i + 1], currPos, holdParams.radius) |
|
|
: this.getEllipseEntryPoint(originalPositions[i + 1], currPos, holdParams.semiMajor ?? 500, holdParams.semiMinor ?? 300, ((holdParams.headingDeg || 0) * Math.PI) / 180); |
|
|
: this.getEllipseEntryPoint(originalPositions[i + 1], currPos, holdParams.semiMajor ?? 500, holdParams.semiMinor ?? 300, ((holdParams.headingDeg || 0) * Math.PI) / 180); |
|
|
} |
|
|
} |
|
|
const radius = this.getWaypointRadius(wp); |
|
|
const effectiveAngle = this.getEffectiveTurnAngle(wp, i, waypoints.length); |
|
|
|
|
|
const radius = this.getWaypointRadius({ ...wp, turnAngle: effectiveAngle }); |
|
|
if (i < waypoints.length - 1 && radius > 0 && nextLogical) { |
|
|
if (i < waypoints.length - 1 && radius > 0 && nextLogical) { |
|
|
const arcPoints = this.computeArcPositions(lastPos, currPos, nextLogical, radius); |
|
|
const arcPoints = this.computeArcPositions(lastPos, currPos, nextLogical, radius); |
|
|
arcPoints.forEach(p => path.push(toLngLatAlt(p))); |
|
|
arcPoints.forEach(p => path.push(toLngLatAlt(p))); |
|
|
@ -3187,7 +3222,12 @@ export default { |
|
|
} |
|
|
} |
|
|
const pos = this.getClickPositionWithHeight(movement.endPosition, this.waypointDragging.originalAlt); |
|
|
const pos = this.getClickPositionWithHeight(movement.endPosition, this.waypointDragging.originalAlt); |
|
|
if (pos) { |
|
|
if (pos) { |
|
|
|
|
|
const entityId = this.waypointDragging.entity.id || ''; |
|
|
|
|
|
if (entityId.endsWith('_entry') || entityId.endsWith('_exit')) { |
|
|
|
|
|
this.waypointDragPreview = { routeId: this.waypointDragging.routeId, dbId: this.waypointDragging.dbId, position: pos }; |
|
|
|
|
|
} else { |
|
|
this.waypointDragging.entity.position = pos; |
|
|
this.waypointDragging.entity.position = pos; |
|
|
|
|
|
} |
|
|
if (this.viewer.scene.requestRender) this.viewer.scene.requestRender(); |
|
|
if (this.viewer.scene.requestRender) this.viewer.scene.requestRender(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -3196,7 +3236,12 @@ export default { |
|
|
if (this.waypointDragging) { |
|
|
if (this.waypointDragging) { |
|
|
const pos = this.getClickPositionWithHeight(movement.endPosition, this.waypointDragging.originalAlt); |
|
|
const pos = this.getClickPositionWithHeight(movement.endPosition, this.waypointDragging.originalAlt); |
|
|
if (pos) { |
|
|
if (pos) { |
|
|
|
|
|
const entityId = this.waypointDragging.entity.id || ''; |
|
|
|
|
|
if (entityId.endsWith('_entry') || entityId.endsWith('_exit')) { |
|
|
|
|
|
this.waypointDragPreview = { routeId: this.waypointDragging.routeId, dbId: this.waypointDragging.dbId, position: pos }; |
|
|
|
|
|
} else { |
|
|
this.waypointDragging.entity.position = pos; |
|
|
this.waypointDragging.entity.position = pos; |
|
|
|
|
|
} |
|
|
if (this.viewer.scene.requestRender) this.viewer.scene.requestRender(); |
|
|
if (this.viewer.scene.requestRender) this.viewer.scene.requestRender(); |
|
|
} |
|
|
} |
|
|
return; |
|
|
return; |
|
|
@ -3229,7 +3274,15 @@ export default { |
|
|
const entity = this.waypointDragging.entity; |
|
|
const entity = this.waypointDragging.entity; |
|
|
const routeId = this.waypointDragging.routeId; |
|
|
const routeId = this.waypointDragging.routeId; |
|
|
const dbId = this.waypointDragging.dbId; |
|
|
const dbId = this.waypointDragging.dbId; |
|
|
const pos = entity.position.getValue(Cesium.JulianDate.now()); |
|
|
const entityId = entity.id || ''; |
|
|
|
|
|
let pos; |
|
|
|
|
|
if (entityId.endsWith('_entry') || entityId.endsWith('_exit')) { |
|
|
|
|
|
pos = this.waypointDragPreview && this.waypointDragPreview.routeId === routeId && this.waypointDragPreview.dbId === dbId |
|
|
|
|
|
? this.waypointDragPreview.position : entity.position.getValue(Cesium.JulianDate.now()); |
|
|
|
|
|
this.waypointDragPreview = null; |
|
|
|
|
|
} else { |
|
|
|
|
|
pos = entity.position.getValue(Cesium.JulianDate.now()); |
|
|
|
|
|
} |
|
|
const ll = this.cartesianToLatLngAlt(pos); |
|
|
const ll = this.cartesianToLatLngAlt(pos); |
|
|
if (ll) { |
|
|
if (ll) { |
|
|
this.$emit('waypoint-position-changed', { dbId, routeId, lat: ll.lat, lng: ll.lng, alt: ll.alt }); |
|
|
this.$emit('waypoint-position-changed', { dbId, routeId, lat: ll.lat, lng: ll.lng, alt: ll.alt }); |
|
|
@ -3237,6 +3290,7 @@ export default { |
|
|
this.lastClickWasDrag = true; |
|
|
this.lastClickWasDrag = true; |
|
|
this.waypointDragging = null; |
|
|
this.waypointDragging = null; |
|
|
} |
|
|
} |
|
|
|
|
|
this.waypointDragPreview = null; |
|
|
this.waypointDragPending = null; |
|
|
this.waypointDragPending = null; |
|
|
}, Cesium.ScreenSpaceEventType.LEFT_UP); |
|
|
}, Cesium.ScreenSpaceEventType.LEFT_UP); |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
|