添加飞机路由

This commit is contained in:
renna 2025-07-15 17:46:00 +08:00
parent ca53570ff8
commit 31304a5ff5
2 changed files with 379 additions and 8 deletions

View File

@ -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}`);
//
// OpenLayers0
// heading0
// 72heading=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;
// LINESTRINGrouteGeometry
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;
//
// OpenLayers0
// heading0
// 72heading=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, //
})
});
}

View File

@ -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>