CollisionAvoidance/tools/map_websocket.html

396 lines
14 KiB
HTML
Raw Blame History

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