添加飞机路由
This commit is contained in:
parent
ca53570ff8
commit
31304a5ff5
@ -47,9 +47,10 @@
|
||||
import { ref, onMounted, onUnmounted, watch, onActivated, onDeactivated } from 'vue';
|
||||
import { Vector as VectorSource } from 'ol/source';
|
||||
import { Vector as VectorLayer } from 'ol/layer';
|
||||
import { Style, Icon } from 'ol/style';
|
||||
import { Style, Icon, Stroke, Fill, Circle, Text } from 'ol/style';
|
||||
import Feature from 'ol/Feature';
|
||||
import Point from 'ol/geom/Point';
|
||||
import { LineString } from 'ol/geom';
|
||||
import { transform } from 'ol/proj';
|
||||
import WebSocketService, { createWebSocket, resetWebSocketInstance } from '../../../utils/websocket.js';
|
||||
|
||||
@ -132,6 +133,11 @@ let vehicleSource = null;
|
||||
// 车辆超速状态超时计时器存储
|
||||
const speedViolationTimers = {};
|
||||
|
||||
// 飞机路线数据存储
|
||||
const aircraftRoutes = ref({}); // 存储飞机路线数据,键为flightNo
|
||||
let aircraftRouteLayer = null; // 飞机路线图层
|
||||
let aircraftRouteSource = null; // 飞机路线数据源
|
||||
|
||||
// 动态收集所有车辆/航空器类别
|
||||
const vehicleCategories = ref({
|
||||
'AIRCRAFT_IN': { visible: true, showLabel: true, name: '滑入航空器' },
|
||||
@ -188,6 +194,26 @@ function createVehicleLayer() {
|
||||
props.map.on('click', handleMapClick);
|
||||
}
|
||||
|
||||
// 创建飞机路线图层
|
||||
function createAircraftRouteLayer() {
|
||||
if (!props.map) return;
|
||||
|
||||
aircraftRouteSource = new VectorSource();
|
||||
|
||||
aircraftRouteLayer = new VectorLayer({
|
||||
source: aircraftRouteSource,
|
||||
zIndex: 15, // 确保在车辆图层下方
|
||||
style: new Style({
|
||||
stroke: new Stroke({
|
||||
color: '#3388ff',
|
||||
width: 3
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
props.map.addLayer(aircraftRouteLayer);
|
||||
}
|
||||
|
||||
// 处理地图点击事件
|
||||
function handleMapClick(event) {
|
||||
const feature = props.map.forEachFeatureAtPixel(event.pixel, function(feature) {
|
||||
@ -248,9 +274,12 @@ function updateVehiclePosition(vehicleData) {
|
||||
|
||||
const { object_id, object_type, position, heading, speed } = vehicleData;
|
||||
|
||||
// 计算正确的旋转角度,不再减去72度
|
||||
const rotationRad = (heading * Math.PI) / 180;
|
||||
console.log(`车辆${object_id}的heading值: ${heading}, 计算的旋转角度: ${rotationRad} 弧度, ${rotationRad * 180 / Math.PI} 度`);
|
||||
// 计算正确的旋转角度
|
||||
// 在OpenLayers中,0度是向上,顺时针增加
|
||||
// 而heading是0度为北,顺时针增加
|
||||
// 由于地图旋转了72度,heading=72对应地图上方
|
||||
const rotationRad = ((heading - 72) * Math.PI) / 180;
|
||||
console.log(`车辆${object_id}的heading值: ${heading}, 旋转角度计算: (${heading} - 72) * π/180 = ${rotationRad} 弧度, ${(heading - 72)}度, 车辆类型: ${object_type}`);
|
||||
|
||||
let coordinates;
|
||||
|
||||
@ -463,6 +492,11 @@ function handleWsMessage(message) {
|
||||
console.log('收到车辆控制指令:', data.payload);
|
||||
break;
|
||||
|
||||
case 'aircraftRouteUpdate':
|
||||
console.log('收到飞机路由更新:', data.data);
|
||||
handleAircraftRouteUpdate(data.data);
|
||||
break;
|
||||
|
||||
default:
|
||||
// 其他类型的消息可以根据需要处理
|
||||
console.log(`未知消息类型: ${data.type}`, data);
|
||||
@ -487,6 +521,269 @@ function handlePositionUpdate(payload) {
|
||||
updateVehiclePosition(payload);
|
||||
}
|
||||
|
||||
// 处理飞机路由更新消息
|
||||
function handleAircraftRouteUpdate(payload) {
|
||||
if (!payload || !payload.flightNo || !payload.routeGeometry) {
|
||||
console.error('飞机路由更新消息格式错误:', payload);
|
||||
return;
|
||||
}
|
||||
|
||||
const { flightNo, routeType, routeStatus, routeGeometry } = payload;
|
||||
|
||||
// 解析LINESTRING格式的routeGeometry
|
||||
const coordinates = parseLineStringGeometry(routeGeometry);
|
||||
if (!coordinates || coordinates.length === 0) {
|
||||
console.error('解析routeGeometry失败:', routeGeometry);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新或创建路线
|
||||
updateAircraftRoute(flightNo, coordinates, routeType, routeStatus);
|
||||
}
|
||||
|
||||
// 解析LINESTRING格式的字符串,提取坐标点
|
||||
function parseLineStringGeometry(lineString) {
|
||||
// 移除LINESTRING前缀和括号
|
||||
const coordsString = lineString.replace(/LINESTRING\s*\(|\)/gi, '');
|
||||
|
||||
// 分割坐标点
|
||||
const coordPairs = coordsString.split(',').map(pair => pair.trim());
|
||||
|
||||
// 解析每个坐标点
|
||||
return coordPairs.map(pair => {
|
||||
const [lon, lat] = pair.split(' ').map(Number);
|
||||
return [lon, lat];
|
||||
});
|
||||
}
|
||||
|
||||
// 更新或创建飞机路线
|
||||
function updateAircraftRoute(flightNo, coordinates, routeType, routeStatus) {
|
||||
if (!aircraftRouteSource || !props.map) return;
|
||||
|
||||
// 转换坐标系
|
||||
const projectedCoords = coordinates.map(coord =>
|
||||
transform(coord, 'EPSG:4326', props.map.getView().getProjection())
|
||||
);
|
||||
|
||||
// 获取起点和终点坐标
|
||||
const startPoint = projectedCoords[0];
|
||||
const endPoint = projectedCoords[projectedCoords.length - 1];
|
||||
|
||||
// 检查是否已存在该航班的路线
|
||||
let feature = aircraftRouteSource.getFeatureById(flightNo);
|
||||
let startFeature = aircraftRouteSource.getFeatureById(`${flightNo}-start`);
|
||||
let endFeature = aircraftRouteSource.getFeatureById(`${flightNo}-end`);
|
||||
|
||||
if (!feature) {
|
||||
// 创建新的LineString几何体
|
||||
const lineGeometry = new LineString(projectedCoords);
|
||||
|
||||
// 创建新的Feature
|
||||
feature = new Feature({
|
||||
geometry: lineGeometry,
|
||||
name: `Flight ${flightNo} Route`,
|
||||
flightNo: flightNo,
|
||||
routeType: routeType,
|
||||
routeStatus: routeStatus
|
||||
});
|
||||
|
||||
// 设置ID和样式
|
||||
feature.setId(flightNo);
|
||||
feature.setStyle(getRouteStyle(routeType, routeStatus));
|
||||
|
||||
// 添加到数据源
|
||||
aircraftRouteSource.addFeature(feature);
|
||||
|
||||
// 创建起点标记
|
||||
createRoutePointMarker(flightNo, 'start', startPoint, routeType);
|
||||
|
||||
// 创建终点标记
|
||||
createRoutePointMarker(flightNo, 'end', endPoint, routeType);
|
||||
|
||||
// 存储路线信息
|
||||
aircraftRoutes.value[flightNo] = {
|
||||
flightNo: flightNo,
|
||||
routeType: routeType,
|
||||
routeStatus: routeStatus,
|
||||
coordinates: projectedCoords,
|
||||
feature: feature
|
||||
};
|
||||
} else {
|
||||
// 更新现有Feature的几何体和属性
|
||||
feature.getGeometry().setCoordinates(projectedCoords);
|
||||
feature.set('routeType', routeType);
|
||||
feature.set('routeStatus', routeStatus);
|
||||
|
||||
// 更新样式
|
||||
feature.setStyle(getRouteStyle(routeType, routeStatus));
|
||||
|
||||
// 更新起点标记位置
|
||||
updateRoutePointMarker(flightNo, 'start', startPoint);
|
||||
|
||||
// 更新终点标记位置
|
||||
updateRoutePointMarker(flightNo, 'end', endPoint);
|
||||
|
||||
// 更新存储的路线信息
|
||||
aircraftRoutes.value[flightNo] = {
|
||||
...aircraftRoutes.value[flightNo],
|
||||
routeType: routeType,
|
||||
routeStatus: routeStatus,
|
||||
coordinates: projectedCoords
|
||||
};
|
||||
}
|
||||
|
||||
// 如果路线状态为完成,可以考虑在一段时间后移除路线
|
||||
if (routeStatus === 'COMPLETE') {
|
||||
setTimeout(() => {
|
||||
removeAircraftRoute(flightNo);
|
||||
}, 60000); // 1分钟后移除
|
||||
}
|
||||
}
|
||||
|
||||
// 根据路线类型和状态获取样式
|
||||
function getRouteStyle(routeType, routeStatus) {
|
||||
let color = '#3388ff'; // 默认蓝色
|
||||
let width = 3;
|
||||
let lineDash = undefined;
|
||||
|
||||
// 根据路线类型设置颜色
|
||||
if (routeType === 'IN') {
|
||||
color = '#292C38'; // 深蓝色表示滑入
|
||||
} else if (routeType === 'OUT') {
|
||||
color = '#3388ff'; // 蓝色表示滑出
|
||||
}
|
||||
|
||||
// // 根据路线状态设置线型
|
||||
// if (routeStatus === 'PLANNED') {
|
||||
// lineDash = [4, 8]; // 计划中的路线使用虚线
|
||||
// width = 2;
|
||||
// } else if (routeStatus === 'ACTIVE') {
|
||||
// width = 4; // 活动中的路线使用粗线
|
||||
// } else if (routeStatus === 'COMPLETE') {
|
||||
// width = 2; // 完成的路线使用细线
|
||||
// color = color + '80'; // 添加透明度
|
||||
// }
|
||||
|
||||
return new Style({
|
||||
stroke: new Stroke({
|
||||
color: color,
|
||||
width: width,
|
||||
lineDash: lineDash
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 获取点样式(起点或终点)
|
||||
function getPointStyle(pointType, routeType, flightNo) {
|
||||
// 根据路线类型选择颜色
|
||||
let color = '#3388ff'; // 默认蓝色
|
||||
if (routeType === 'IN') {
|
||||
color = '#292C38'; // 深蓝色表示滑入
|
||||
} else if (routeType === 'OUT') {
|
||||
color = '#3388ff'; // 蓝色表示滑出
|
||||
}
|
||||
|
||||
// 创建圆点样式
|
||||
const circleStyle = new Style({
|
||||
image: new Circle({
|
||||
radius: 6,
|
||||
fill: new Fill({
|
||||
color: color
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: '#ffffff',
|
||||
width: 2
|
||||
})
|
||||
}),
|
||||
// 添加文本标签
|
||||
text: new Text({
|
||||
text: pointType === 'start' ? '起点' : '终点',
|
||||
font: 'bold 12px Arial',
|
||||
fill: new Fill({
|
||||
color: '#ffffff'
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: '#000000',
|
||||
width: 3
|
||||
}),
|
||||
offsetX: 0,
|
||||
offsetY: pointType === 'start' ? -15 : -15, // 文字位置偏移
|
||||
textAlign: 'center',
|
||||
textBaseline: 'bottom'
|
||||
}),
|
||||
zIndex: 10 // 确保点标记显示在线的上方
|
||||
});
|
||||
|
||||
return circleStyle;
|
||||
}
|
||||
|
||||
// 创建路线点标记(起点或终点)
|
||||
function createRoutePointMarker(flightNo, pointType, coordinates, routeType) {
|
||||
// 创建点几何体
|
||||
const pointGeometry = new Point(coordinates);
|
||||
|
||||
// 创建Feature
|
||||
const pointFeature = new Feature({
|
||||
geometry: pointGeometry,
|
||||
name: `${flightNo} ${pointType === 'start' ? '起点' : '终点'}`,
|
||||
flightNo: flightNo,
|
||||
pointType: pointType
|
||||
});
|
||||
|
||||
// 设置ID
|
||||
pointFeature.setId(`${flightNo}-${pointType}`);
|
||||
|
||||
// 设置样式
|
||||
pointFeature.setStyle(getPointStyle(pointType, routeType, flightNo));
|
||||
|
||||
// 添加到数据源
|
||||
aircraftRouteSource.addFeature(pointFeature);
|
||||
|
||||
return pointFeature;
|
||||
}
|
||||
|
||||
// 更新路线点标记位置
|
||||
function updateRoutePointMarker(flightNo, pointType, coordinates) {
|
||||
const pointFeature = aircraftRouteSource.getFeatureById(`${flightNo}-${pointType}`);
|
||||
|
||||
if (pointFeature) {
|
||||
// 更新几何体
|
||||
pointFeature.getGeometry().setCoordinates(coordinates);
|
||||
} else {
|
||||
// 如果不存在,创建新的点标记
|
||||
const routeType = aircraftRoutes.value[flightNo]?.routeType || 'UNKNOWN';
|
||||
createRoutePointMarker(flightNo, pointType, coordinates, routeType);
|
||||
}
|
||||
}
|
||||
|
||||
// 移除飞机路线
|
||||
function removeAircraftRoute(flightNo) {
|
||||
if (!aircraftRouteSource) return;
|
||||
|
||||
// 移除路线Feature
|
||||
const feature = aircraftRouteSource.getFeatureById(flightNo);
|
||||
if (feature) {
|
||||
aircraftRouteSource.removeFeature(feature);
|
||||
}
|
||||
|
||||
// 移除起点Feature
|
||||
const startFeature = aircraftRouteSource.getFeatureById(`${flightNo}-start`);
|
||||
if (startFeature) {
|
||||
aircraftRouteSource.removeFeature(startFeature);
|
||||
}
|
||||
|
||||
// 移除终点Feature
|
||||
const endFeature = aircraftRouteSource.getFeatureById(`${flightNo}-end`);
|
||||
if (endFeature) {
|
||||
aircraftRouteSource.removeFeature(endFeature);
|
||||
}
|
||||
|
||||
// 从存储中移除
|
||||
if (aircraftRoutes.value[flightNo]) {
|
||||
delete aircraftRoutes.value[flightNo];
|
||||
}
|
||||
}
|
||||
|
||||
// 处理冲突告警和预警
|
||||
function handlePathConflictAlert(payload) {
|
||||
if (!payload) {
|
||||
@ -888,6 +1185,15 @@ function cleanup() {
|
||||
vehicleLayer = null;
|
||||
}
|
||||
|
||||
// 移除飞机路线图层
|
||||
if (aircraftRouteLayer && props.map) {
|
||||
props.map.removeLayer(aircraftRouteLayer);
|
||||
aircraftRouteLayer = null;
|
||||
}
|
||||
|
||||
// 清空飞机路线数据
|
||||
aircraftRoutes.value = {};
|
||||
|
||||
// 移除标签
|
||||
if (props.map && labelSystem.value) {
|
||||
Object.values(vehicles.value).forEach(vehicle => {
|
||||
@ -907,6 +1213,7 @@ onMounted(() => {
|
||||
// 创建图层并连接WebSocket
|
||||
if (props.map) {
|
||||
createVehicleLayer();
|
||||
createAircraftRouteLayer(); // 创建飞机路线图层
|
||||
}
|
||||
|
||||
// 连接WebSocket
|
||||
@ -964,6 +1271,7 @@ onDeactivated(() => {
|
||||
watch(() => props.map, (newMap) => {
|
||||
if (newMap) {
|
||||
createVehicleLayer();
|
||||
createAircraftRouteLayer(); // 创建飞机路线图层
|
||||
}
|
||||
});
|
||||
|
||||
@ -1084,6 +1392,42 @@ defineExpose({
|
||||
if (animationSystem.value) {
|
||||
animationSystem.value.resetAnimations();
|
||||
}
|
||||
},
|
||||
|
||||
// 飞机路线相关方法
|
||||
getAircraftRoutes() {
|
||||
return aircraftRoutes.value;
|
||||
},
|
||||
|
||||
showAircraftRoute(flightNo) {
|
||||
const route = aircraftRoutes.value[flightNo];
|
||||
if (route && route.feature) {
|
||||
// 定位到路线
|
||||
const extent = route.feature.getGeometry().getExtent();
|
||||
props.map.getView().fit(extent, {
|
||||
padding: [50, 50, 50, 50],
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setAircraftRouteVisibility(visible) {
|
||||
if (aircraftRouteLayer) {
|
||||
aircraftRouteLayer.setVisible(visible);
|
||||
}
|
||||
},
|
||||
|
||||
// 飞机路线点标记相关方法
|
||||
createRoutePointMarker(flightNo, pointType, coordinates, routeType) {
|
||||
return createRoutePointMarker(flightNo, pointType, coordinates, routeType);
|
||||
},
|
||||
|
||||
updateRoutePointMarker(flightNo, pointType, coordinates) {
|
||||
return updateRoutePointMarker(flightNo, pointType, coordinates);
|
||||
},
|
||||
|
||||
removeAircraftRoute(flightNo) {
|
||||
return removeAircraftRoute(flightNo);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1107,14 +1451,20 @@ function defaultGetVehicleStyle(id, _speed, heading) {
|
||||
function createDefaultStyle(iconSrc, heading) {
|
||||
// 确保heading是有效的数值并正确转换为弧度
|
||||
const validHeading = heading !== undefined ? Number(heading) : 0;
|
||||
const rotationRad = (validHeading * Math.PI) / 180;
|
||||
|
||||
// 计算正确的旋转角度
|
||||
// 在OpenLayers中,0度是向上,顺时针增加
|
||||
// 而heading是0度为北,顺时针增加
|
||||
// 由于地图旋转了72度,heading=72对应地图上方
|
||||
const rotationRad = ((validHeading - 72) * Math.PI) / 180;
|
||||
// console.log(`createDefaultStyle: heading=${validHeading}, 旋转角度计算: (${validHeading} - 72) * π/180 = ${rotationRad} 弧度, ${(validHeading - 72)}度`);
|
||||
|
||||
return new Style({
|
||||
image: new Icon({
|
||||
src: iconSrc, // 使用传入的图标源
|
||||
scale: 1.5,
|
||||
anchor: [0.5, 0.5],
|
||||
rotation: rotationRad, // 正确应用旋转角度
|
||||
rotation: rotationRad, // 应用计算后的旋转角度
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@ -263,7 +264,12 @@
|
||||
log('collisionLog', `碰撞预警: ${message.payload?.message || JSON.stringify(message.payload)}`, 'error');
|
||||
break;
|
||||
case 'rule_violation':
|
||||
log('collisionLog', `规则违规: ${message.payload?.ruleType || '未知规则'}`, 'warning');
|
||||
const vehicleLicense = message.payload?.vehicleLicense || '未知车辆';
|
||||
const ruleName = message.payload?.ruleName || '未知规则';
|
||||
const violationType = message.payload?.violationType || '未知类型';
|
||||
const alertLevel = message.payload?.alertLevel || '未知级别';
|
||||
const description = message.payload?.description || '无描述';
|
||||
log('collisionLog', `规则违规: ${vehicleLicense} - ${ruleName} (${violationType}/${alertLevel}): ${description}`, 'warning');
|
||||
break;
|
||||
case 'rule_execution_status':
|
||||
log('collisionLog', `规则执行状态: ${message.payload?.status || '未知状态'}`, 'info');
|
||||
@ -277,6 +283,19 @@
|
||||
case 'traffic_light_status':
|
||||
log('collisionLog', `红绿灯状态: ${message.payload?.intersection_id || '未知路口'}`, 'info');
|
||||
break;
|
||||
case 'aircraftRouteUpdate':
|
||||
const flightNo = message.payload?.flightNo || '未知航班';
|
||||
const routeType = message.payload?.routeType || '未知路由';
|
||||
const routeStatus = message.payload?.status || '未知状态';
|
||||
log('collisionLog', `航空器路由更新: ${flightNo} - ${routeType} (${routeStatus})`, 'info');
|
||||
break;
|
||||
case 'path_conflict_alert':
|
||||
const object1 = message.payload?.object1Name || '未知对象1';
|
||||
const object2 = message.payload?.object2Name || '未知对象2';
|
||||
const conflictType = message.payload?.alertType || '未知类型';
|
||||
const conflictLevel = message.payload?.alertLevel || '未知级别';
|
||||
log('collisionLog', `路径冲突告警: ${object1} vs ${object2} (${conflictType}/${conflictLevel})`, 'error');
|
||||
break;
|
||||
default:
|
||||
log('collisionLog', `未知消息类型: ${message.type}`, 'info');
|
||||
}
|
||||
@ -318,4 +337,6 @@
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user