396 lines
14 KiB
HTML
396 lines
14 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>机场车辆监控</title>
|
||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
|
||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||
<style>
|
||
body {
|
||
margin: 0;
|
||
padding: 20px;
|
||
font-family: Arial, sans-serif;
|
||
}
|
||
.container {
|
||
display: flex;
|
||
gap: 20px;
|
||
}
|
||
#map {
|
||
height: 800px;
|
||
width: 60%;
|
||
border: 1px solid #ccc;
|
||
}
|
||
#messages {
|
||
width: 40%;
|
||
height: 800px;
|
||
overflow-y: auto;
|
||
border: 1px solid #ccc;
|
||
padding: 10px;
|
||
font-family: monospace;
|
||
}
|
||
.controls {
|
||
margin-top: 10px;
|
||
}
|
||
.error { color: red; }
|
||
.success { color: green; }
|
||
.info { color: blue; }
|
||
.position { color: #666; }
|
||
.warning { color: #f90; }
|
||
.command { color: #800080; }
|
||
.vehicle-icon {
|
||
width: 10px;
|
||
height: 10px;
|
||
background-color: black;
|
||
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
||
border: 2px solid white;
|
||
}
|
||
.aircraft-icon {
|
||
width: 50px;
|
||
height: 50px;
|
||
background-color: purple;
|
||
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
||
border: 2px solid white;
|
||
}
|
||
.special-vehicle-icon {
|
||
width: 10px;
|
||
height: 10px;
|
||
background-color: orange;
|
||
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
||
border: 2px solid white;
|
||
}
|
||
.intersection-icon {
|
||
width: 30px;
|
||
height: 30px;
|
||
background-color: #666;
|
||
clip-path: polygon(40% 0%, 60% 0%, 60% 40%, 100% 40%, 100% 60%, 60% 60%, 60% 100%, 40% 100%, 40% 60%, 0% 60%, 0% 40%, 40% 40%);
|
||
border: 2px solid white;
|
||
}
|
||
.traffic-light {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
border: 1px solid white;
|
||
z-index: 1000;
|
||
}
|
||
.traffic-light-red {
|
||
background-color: red;
|
||
}
|
||
.traffic-light-green {
|
||
background-color: green;
|
||
}
|
||
.distance-label {
|
||
background: none;
|
||
border: none;
|
||
color: #666;
|
||
font-size: 12px;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h2>机场车辆监控系统</h2>
|
||
<div class="container">
|
||
<div id="map"></div>
|
||
<div id="messages"></div>
|
||
</div>
|
||
<div class="controls">
|
||
<button onclick="connect()">连接</button>
|
||
<button onclick="disconnect()">断开</button>
|
||
<button onclick="clearMessages()">清空日志</button>
|
||
</div>
|
||
|
||
<script>
|
||
let ws = null;
|
||
const messagesDiv = document.getElementById('messages');
|
||
|
||
// 初始化地图
|
||
const map = L.map('map').setView([36.361999, 120.088503], 17);
|
||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||
maxZoom: 19,
|
||
attribution: '© OpenStreetMap contributors'
|
||
}).addTo(map);
|
||
|
||
// 定义路口坐标
|
||
const WEST_INTERSECTION = {
|
||
latitude: 36.361999,
|
||
longitude: 120.086003
|
||
};
|
||
const EAST_INTERSECTION = {
|
||
latitude: 36.361999,
|
||
longitude: 120.089003
|
||
};
|
||
|
||
// 存储所有标记
|
||
const markers = new Map();
|
||
const trafficLights = new Map();
|
||
const intersections = new Map();
|
||
|
||
// 创建自定义图标
|
||
function createIcon(className) {
|
||
let size;
|
||
if (className.includes('aircraft')) {
|
||
size = [50, 50]; // 50米正方形
|
||
} else if (className.includes('vehicle')) {
|
||
size = [10, 10]; // 10米正方形
|
||
} else if (className.includes('traffic-light')) {
|
||
size = [10, 10]; // 10像素的红绿灯
|
||
} else {
|
||
size = [20, 20]; // 其他图标保持原样
|
||
}
|
||
|
||
return L.divIcon({
|
||
className: className,
|
||
iconSize: size,
|
||
iconAnchor: [size[0]/2, size[1]/2] // 设置图标锚点为中心
|
||
});
|
||
}
|
||
|
||
function log(message, type = 'info') {
|
||
const div = document.createElement('div');
|
||
div.className = type;
|
||
div.textContent = `${new Date().toLocaleTimeString()} - ${message}`;
|
||
messagesDiv.appendChild(div);
|
||
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
||
}
|
||
|
||
function clearMessages() {
|
||
messagesDiv.innerHTML = '';
|
||
}
|
||
|
||
function updatePosition(data) {
|
||
const id = data.object_id;
|
||
const position = [data.position.latitude, data.position.longitude];
|
||
let iconClass;
|
||
|
||
// 根据ID前缀确定图标类型
|
||
if (data.object_type === 'aircraft') {
|
||
iconClass = 'aircraft-icon'; // 六形
|
||
} else if (id.startsWith('TQ')) {
|
||
iconClass = 'special-vehicle-icon'; // 正方形
|
||
} else {
|
||
iconClass = 'vehicle-icon'; // 三角形
|
||
}
|
||
|
||
let marker = markers.get(id);
|
||
if (!marker) {
|
||
// 创建新标记
|
||
marker = L.marker(position, {
|
||
icon: createIcon(iconClass)
|
||
}).addTo(map);
|
||
marker.bindTooltip(id); // 添加标签显示ID
|
||
markers.set(id, marker);
|
||
} else {
|
||
// 更新现有标记位置
|
||
marker.setLatLng(position);
|
||
}
|
||
|
||
// 更新标记旋转(根据航向)
|
||
if (data.heading !== undefined) {
|
||
marker.setRotationAngle(data.heading);
|
||
}
|
||
}
|
||
|
||
function updateTrafficLight(data) {
|
||
const id = data.id;
|
||
const position = [data.position.latitude, data.position.longitude];
|
||
const state = data.status === 0 ? 'red' : 'green';
|
||
|
||
let light = trafficLights.get(id);
|
||
if (!light) {
|
||
light = L.marker(position, {
|
||
icon: createIcon(`traffic-light traffic-light-${state}`)
|
||
}).addTo(map);
|
||
light.bindTooltip(id);
|
||
trafficLights.set(id, light);
|
||
} else {
|
||
light.setIcon(createIcon(`traffic-light traffic-light-${state}`));
|
||
}
|
||
}
|
||
|
||
function connect() {
|
||
if (ws) {
|
||
log('已经连接,请先断开', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
ws = new WebSocket('ws://localhost:8010');
|
||
|
||
ws.onopen = () => {
|
||
log('连接成功', 'success');
|
||
};
|
||
|
||
ws.onclose = () => {
|
||
log('连接关闭', 'info');
|
||
ws = null;
|
||
};
|
||
|
||
ws.onerror = (error) => {
|
||
log('发生错误: ' + error, 'error');
|
||
};
|
||
|
||
ws.onmessage = (event) => {
|
||
try {
|
||
const data = JSON.parse(event.data);
|
||
const formattedData = JSON.stringify(data, null, 2);
|
||
|
||
// 根据消息类型使用不同的样式和处理方式
|
||
let type = 'info';
|
||
let message = '收到消息:\n' + formattedData;
|
||
|
||
switch (data.type) {
|
||
case 'position_update':
|
||
type = 'position';
|
||
updatePosition(data);
|
||
break;
|
||
case 'traffic_light_status':
|
||
updateTrafficLight(data);
|
||
break;
|
||
case 'collision_warning':
|
||
type = 'warning';
|
||
break;
|
||
case 'vehicle_command':
|
||
type = 'command';
|
||
// 为控制指令添加中文描述
|
||
const commandTypes = {
|
||
'SIGNAL': '信号灯指令',
|
||
'ALERT': '告警指令',
|
||
'WARNING': '预警指令',
|
||
'RESUME': '恢复指令'
|
||
};
|
||
const reasons = {
|
||
'TRAFFIC_LIGHT': '红绿灯控制',
|
||
'AIRCRAFT_CROSSING': '航空器交叉',
|
||
'SPECIAL_VEHICLE': '特勤车辆',
|
||
'AIRCRAFT_PUSH': '航空器推出',
|
||
'RESUME_TRAFFIC': '恢复通行'
|
||
};
|
||
message = `收到车辆控制指令:\n车辆: ${data.vehicleId}\n` +
|
||
`指令类型: ${commandTypes[data.commandType] || data.commandType}\n` +
|
||
`原因: ${reasons[data.reason] || data.reason}\n` +
|
||
(data.targetLatitude !== undefined ? `目标位置: (${data.targetLatitude}, ${data.targetLongitude})\n` : '') +
|
||
(data.signalState ? `信号灯状态: ${data.signalState}\n` : '') +
|
||
(data.intersectionId ? `路口ID: ${data.intersectionId}\n` : '') +
|
||
`时间戳: ${new Date(data.timestamp/1000000).toLocaleString()}`;
|
||
break;
|
||
}
|
||
|
||
log(message, type);
|
||
} catch (e) {
|
||
log('收到消息: ' + event.data, 'info');
|
||
}
|
||
};
|
||
} catch (error) {
|
||
log('连接失败: ' + error, 'error');
|
||
}
|
||
}
|
||
|
||
function disconnect() {
|
||
if (!ws) {
|
||
log('未连接', 'error');
|
||
return;
|
||
}
|
||
|
||
ws.close();
|
||
ws = null;
|
||
|
||
// 清除所有标记
|
||
markers.forEach(marker => map.removeLayer(marker));
|
||
markers.clear();
|
||
trafficLights.forEach(light => map.removeLayer(light));
|
||
trafficLights.clear();
|
||
}
|
||
|
||
// 添加道路刻度标记函数
|
||
function addRoadMarks(startPoint, endPoint, isLatitude = false) {
|
||
const start = isLatitude ? startPoint[0] : startPoint[1];
|
||
const end = isLatitude ? endPoint[0] : endPoint[1];
|
||
const fixed = isLatitude ? startPoint[1] : startPoint[0];
|
||
const step = 0.0005; // 约50米
|
||
const direction = start < end ? 1 : -1;
|
||
|
||
for (let i = 0; Math.abs(i) <= Math.abs(end - start); i += step * direction) {
|
||
const position = isLatitude ?
|
||
[start + i, fixed] :
|
||
[fixed, start + i];
|
||
|
||
// 添加刻度线
|
||
const markLine = isLatitude ?
|
||
[[position[0], position[1] - 0.0001], [position[0], position[1] + 0.0001]] :
|
||
[[position[0] - 0.0001, position[1]], [position[0] + 0.0001, position[1]]];
|
||
|
||
L.polyline(markLine, {
|
||
color: '#666',
|
||
weight: 2
|
||
}).addTo(map);
|
||
|
||
// 添加距离标签(以米为单位)
|
||
const distance = Math.abs(Math.round(i / step) * 50);
|
||
if (distance > 0) { // 只显示非零<E99D9E><E99BB6><EFBFBD>离
|
||
const label = L.divIcon({
|
||
className: 'distance-label',
|
||
html: distance + 'm',
|
||
iconSize: [40, 20],
|
||
iconAnchor: [20, 10]
|
||
});
|
||
|
||
L.marker(position, {
|
||
icon: label,
|
||
interactive: false
|
||
}).addTo(map);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 添加道路
|
||
// 东西向主路
|
||
const mainRoadEW = L.polyline([
|
||
[WEST_INTERSECTION.latitude, WEST_INTERSECTION.longitude - 0.002],
|
||
[EAST_INTERSECTION.latitude, EAST_INTERSECTION.longitude + 0.002]
|
||
], {
|
||
color: '#999',
|
||
weight: 8
|
||
}).addTo(map);
|
||
|
||
// 西路口南北向道路
|
||
const westRoadNS = L.polyline([
|
||
[WEST_INTERSECTION.latitude + 0.002, WEST_INTERSECTION.longitude],
|
||
[WEST_INTERSECTION.latitude - 0.002, WEST_INTERSECTION.longitude]
|
||
], {
|
||
color: '#999',
|
||
weight: 8
|
||
}).addTo(map);
|
||
|
||
// 东路口南北向道路
|
||
const eastRoadNS = L.polyline([
|
||
[EAST_INTERSECTION.latitude + 0.002, EAST_INTERSECTION.longitude],
|
||
[EAST_INTERSECTION.latitude - 0.002, EAST_INTERSECTION.longitude]
|
||
], {
|
||
color: '#999',
|
||
weight: 8
|
||
}).addTo(map);
|
||
|
||
// 添加刻度标记
|
||
// 西路口南北向刻度
|
||
addRoadMarks(
|
||
[WEST_INTERSECTION.latitude - 0.002, WEST_INTERSECTION.longitude],
|
||
[WEST_INTERSECTION.latitude + 0.002, WEST_INTERSECTION.longitude],
|
||
true
|
||
);
|
||
|
||
// 东路口南北向刻度
|
||
addRoadMarks(
|
||
[EAST_INTERSECTION.latitude - 0.002, EAST_INTERSECTION.longitude],
|
||
[EAST_INTERSECTION.latitude + 0.002, EAST_INTERSECTION.longitude],
|
||
true
|
||
);
|
||
|
||
// 东西向刻度
|
||
addRoadMarks(
|
||
[WEST_INTERSECTION.latitude, WEST_INTERSECTION.longitude - 0.002],
|
||
[EAST_INTERSECTION.latitude, EAST_INTERSECTION.longitude + 0.002],
|
||
false
|
||
);
|
||
</script>
|
||
</body>
|
||
</html> |