增加红绿灯的两个方向的信号状态处理和前端页面显示
This commit is contained in:
parent
d645dbc486
commit
5f2c0e4d86
@ -9,6 +9,8 @@
|
|||||||
#include "network/TrafficLightHttpServer.h"
|
#include "network/TrafficLightHttpServer.h"
|
||||||
#include "config/SystemConfig.h"
|
#include "config/SystemConfig.h"
|
||||||
#include "types/TrafficLightTypes.h"
|
#include "types/TrafficLightTypes.h"
|
||||||
|
#include "network/WebSocketServer.h"
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
System* System::instance_ = nullptr;
|
System* System::instance_ = nullptr;
|
||||||
|
|
||||||
@ -494,6 +496,16 @@ void System::broadcastPositionUpdate(const MovingObject& obj) {
|
|||||||
" timestamp=", msg.timestamp);
|
" timestamp=", msg.timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增辅助函数:将 SignalStatus 转换为字符串
|
||||||
|
std::string signalStatusToString(SignalStatus status) {
|
||||||
|
switch (status) {
|
||||||
|
case SignalStatus::RED: return "RED";
|
||||||
|
case SignalStatus::GREEN: return "GREEN";
|
||||||
|
case SignalStatus::YELLOW: return "YELLOW";
|
||||||
|
default: return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void System::broadcastTrafficLightStatus(const TrafficLightSignal& signal) {
|
void System::broadcastTrafficLightStatus(const TrafficLightSignal& signal) {
|
||||||
if (!ws_server_) {
|
if (!ws_server_) {
|
||||||
return;
|
return;
|
||||||
@ -505,8 +517,13 @@ void System::broadcastTrafficLightStatus(const TrafficLightSignal& signal) {
|
|||||||
nlohmann::json j = {
|
nlohmann::json j = {
|
||||||
{"type", "traffic_light_status"},
|
{"type", "traffic_light_status"},
|
||||||
{"id", signal.trafficLightId},
|
{"id", signal.trafficLightId},
|
||||||
{"status", static_cast<int>(signal.status)},
|
// 修改: 将 status 改为一个包含 ns 和 ew 状态的对象
|
||||||
{"timestamp", signal.timestamp} };
|
{"status", {
|
||||||
|
{"ns", signalStatusToString(signal.ns_status)},
|
||||||
|
{"ew", signalStatusToString(signal.ew_status)}
|
||||||
|
}},
|
||||||
|
{"timestamp", signal.timestamp}
|
||||||
|
};
|
||||||
|
|
||||||
// 添加路口信息
|
// 添加路口信息
|
||||||
if (intersection) {
|
if (intersection) {
|
||||||
@ -517,12 +534,13 @@ void System::broadcastTrafficLightStatus(const TrafficLightSignal& signal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ws_server_->broadcast(j.dump());
|
ws_server_->broadcast(j.dump());
|
||||||
|
|
||||||
|
// 修改日志记录
|
||||||
Logger::debug("广播红绿灯状态: id=", signal.trafficLightId,
|
Logger::debug("广播红绿灯状态: id=", signal.trafficLightId,
|
||||||
" status=", static_cast<int>(signal.status),
|
", NS_Status=", signalStatusToString(signal.ns_status),
|
||||||
" intersection=", intersection ? intersection->id : "",
|
", EW_Status=", signalStatusToString(signal.ew_status),
|
||||||
" position=(", intersection ? intersection->position.longitude : 0.0, ",",
|
", intersection=", intersection ? intersection->id : "",
|
||||||
intersection ? intersection->position.latitude : 0.0, ")",
|
", timestamp=", signal.timestamp);
|
||||||
" timestamp=", signal.timestamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getRiskLevelString(RiskLevel level) {
|
std::string getRiskLevelString(RiskLevel level) {
|
||||||
@ -774,61 +792,65 @@ bool System::handleSafetyZoneRisk(const Vehicle& vehicle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void System::processPushedTrafficLightData(const nlohmann::json& di_data) {
|
void System::processPushedTrafficLightData(const nlohmann::json& di_data) {
|
||||||
const auto& config = SystemConfig::instance();
|
|
||||||
const std::string target_id = config.simulated_mobile_light_target_intersection_id;
|
|
||||||
|
|
||||||
if (target_id.empty()) {
|
|
||||||
Logger::warning("Target intersection ID for pushed traffic light data is not configured.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用配置的 ID 查找路口信息
|
|
||||||
const Intersection* intersection = intersection_config_.findById(target_id);
|
|
||||||
if (!intersection) {
|
|
||||||
Logger::warning("Configured target intersection ID ", target_id, " not found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析 DI 数据获取当前状态 (简化: 只处理南北向)
|
|
||||||
SignalStatus current_status = SignalStatus::UNKNOWN;
|
|
||||||
try {
|
try {
|
||||||
// DI-13: 北绿 (NS Green)
|
const auto& config = SystemConfig::instance();
|
||||||
|
const std::string targetIntersectionId = config.simulated_mobile_light_target_intersection_id;
|
||||||
|
const Intersection* targetIntersection = intersection_config_.findById(targetIntersectionId);
|
||||||
|
|
||||||
|
if (!targetIntersection) {
|
||||||
|
Logger::warning("Configured target intersection ID not found in intersection config: ", targetIntersectionId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalStatus ns_status = SignalStatus::UNKNOWN;
|
||||||
|
SignalStatus ew_status = SignalStatus::UNKNOWN;
|
||||||
|
|
||||||
if (di_data.contains("DI-13") && di_data.at("DI-13").get<int>() == 1) {
|
if (di_data.contains("DI-13") && di_data.at("DI-13").get<int>() == 1) {
|
||||||
current_status = SignalStatus::GREEN;
|
ns_status = SignalStatus::GREEN;
|
||||||
}
|
} else if (di_data.contains("DI-12") && di_data.at("DI-12").get<int>() == 1) {
|
||||||
// DI-12: 北黄 (NS Yellow)
|
ns_status = SignalStatus::YELLOW;
|
||||||
else if (di_data.contains("DI-12") && di_data.at("DI-12").get<int>() == 1) {
|
} else if (di_data.contains("DI-11") && di_data.at("DI-11").get<int>() == 1) {
|
||||||
current_status = SignalStatus::YELLOW;
|
ns_status = SignalStatus::RED;
|
||||||
}
|
} else {
|
||||||
// DI-11: 北红 (NS Red)
|
|
||||||
else if (di_data.contains("DI-11") && di_data.at("DI-11").get<int>() == 1) {
|
|
||||||
current_status = SignalStatus::RED;
|
|
||||||
}
|
|
||||||
// 可以添加东西向 DI-14 (E/W Red), DI-15 (E/W Yellow), DI-16 (E/W Green) 的解析逻辑
|
|
||||||
else {
|
|
||||||
Logger::warning("Received DI data has unknown or invalid N/S state: ", di_data.dump());
|
Logger::warning("Received DI data has unknown or invalid N/S state: ", di_data.dump());
|
||||||
// 可以选择不广播或广播 UNKNOWN
|
|
||||||
// return; // 如果状态无效则不广播
|
|
||||||
}
|
}
|
||||||
} catch (const json::exception& e) {
|
|
||||||
Logger::error("Error parsing DI data JSON: ", e.what(), ", Data: ", di_data.dump());
|
if (di_data.contains("DI-16") && di_data.at("DI-16").get<int>() == 1) {
|
||||||
return;
|
ew_status = SignalStatus::GREEN;
|
||||||
|
} else if (di_data.contains("DI-15") && di_data.at("DI-15").get<int>() == 1) {
|
||||||
|
ew_status = SignalStatus::YELLOW;
|
||||||
|
} else if (di_data.contains("DI-14") && di_data.at("DI-14").get<int>() == 1) {
|
||||||
|
ew_status = SignalStatus::RED;
|
||||||
|
} else {
|
||||||
|
Logger::warning("Received DI data has unknown or invalid E/W state: ", di_data.dump());
|
||||||
|
}
|
||||||
|
|
||||||
|
TrafficLightSignal signal;
|
||||||
|
signal.trafficLightId = "DI_PUSHED_" + targetIntersectionId;
|
||||||
|
signal.ns_status = ns_status;
|
||||||
|
signal.ew_status = ew_status;
|
||||||
|
signal.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
if (ws_server_) {
|
||||||
|
nlohmann::json messageJson;
|
||||||
|
messageJson["type"] = "intersection_traffic_light_status";
|
||||||
|
messageJson["intersection_id"] = targetIntersection->id;
|
||||||
|
messageJson["position"] = {
|
||||||
|
{"latitude", targetIntersection->position.latitude},
|
||||||
|
{"longitude", targetIntersection->position.longitude}
|
||||||
|
};
|
||||||
|
messageJson["ns_status"] = static_cast<int>(signal.ns_status);
|
||||||
|
messageJson["ew_status"] = static_cast<int>(signal.ew_status);
|
||||||
|
messageJson["timestamp"] = signal.timestamp;
|
||||||
|
|
||||||
|
ws_server_->broadcast(messageJson.dump());
|
||||||
|
Logger::debug("广播路口交通灯状态: ", messageJson.dump());
|
||||||
|
} else {
|
||||||
|
Logger::warning("WebSocket server is not available, cannot broadcast intersection status.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
Logger::error("处理推送的交通信号灯数据时出错: ", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 TrafficLightSignal 结构体
|
|
||||||
TrafficLightSignal signal;
|
|
||||||
signal.trafficLightId = intersection->trafficLightId; // 使用找到的路口的红绿灯 ID
|
|
||||||
signal.status = current_status;
|
|
||||||
signal.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch())
|
|
||||||
.count();
|
|
||||||
signal.intersectionId = intersection->id;
|
|
||||||
signal.latitude = intersection->position.latitude;
|
|
||||||
signal.longitude = intersection->position.longitude;
|
|
||||||
|
|
||||||
Logger::debug("Processing pushed traffic light data for intersection: ", intersection->id,
|
|
||||||
", status: ", static_cast<int>(signal.status));
|
|
||||||
|
|
||||||
// 广播信号状态
|
|
||||||
broadcastTrafficLightStatus(signal);
|
|
||||||
}
|
}
|
||||||
@ -16,6 +16,7 @@
|
|||||||
#include "network/MessageTypes.h"
|
#include "network/MessageTypes.h"
|
||||||
#include "config/IntersectionConfig.h"
|
#include "config/IntersectionConfig.h"
|
||||||
#include "network/TrafficLightHttpServer.h"
|
#include "network/TrafficLightHttpServer.h"
|
||||||
|
#include <nlohmann/json_fwd.hpp>
|
||||||
|
|
||||||
// 前向声明
|
// 前向声明
|
||||||
class DataCollector;
|
class DataCollector;
|
||||||
@ -43,9 +44,12 @@ public:
|
|||||||
const SystemConfig& getSystemConfig() const { return SystemConfig::instance(); }
|
const SystemConfig& getSystemConfig() const { return SystemConfig::instance(); }
|
||||||
const IntersectionConfig& getIntersectionConfig() const { return intersection_config_; }
|
const IntersectionConfig& getIntersectionConfig() const { return intersection_config_; }
|
||||||
|
|
||||||
// 新增: 处理推送的红绿灯数据
|
// 新增: 处理推送的红绿灯数据 (改回接收 json 对象)
|
||||||
void processPushedTrafficLightData(const nlohmann::json& di_data);
|
void processPushedTrafficLightData(const nlohmann::json& di_data);
|
||||||
|
|
||||||
|
// 处理来自数据源的交通信号灯状态
|
||||||
|
void processTrafficLightStatus(const TrafficLightSignal& signal);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void processLoop();
|
void processLoop();
|
||||||
void processCollisions(const std::vector<CollisionRisk>& detectedRisks,
|
void processCollisions(const std::vector<CollisionRisk>& detectedRisks,
|
||||||
|
|||||||
@ -27,7 +27,9 @@ void TrafficLightDetector::processSignal(const TrafficLightSignal& signal,
|
|||||||
for (const auto& vehicle : vehicles) {
|
for (const auto& vehicle : vehicles) {
|
||||||
if (controllable_vehicles_.isControllable(vehicle.vehicleNo)) {
|
if (controllable_vehicles_.isControllable(vehicle.vehicleNo)) {
|
||||||
// 根据信号灯状态发送指令
|
// 根据信号灯状态发送指令
|
||||||
switch (signal.status) {
|
// FIXME: 临时修改 - 仅根据南北向状态发送指令,未考虑车辆方向和东西向状态。
|
||||||
|
// 需要根据车辆行驶方向判断应该参考 ns_status 还是 ew_status。
|
||||||
|
switch (signal.ns_status) {
|
||||||
case SignalStatus::RED:
|
case SignalStatus::RED:
|
||||||
sendSignalCommand(vehicle.vehicleNo, SignalState::RED);
|
sendSignalCommand(vehicle.vehicleNo, SignalState::RED);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -582,8 +582,8 @@ bool HTTPDataSource::parseTrafficLightResponse(const std::string& response, std:
|
|||||||
for (const auto& item : data) {
|
for (const auto& item : data) {
|
||||||
TrafficLightSignal signal;
|
TrafficLightSignal signal;
|
||||||
signal.trafficLightId = item["id"].get<std::string>();
|
signal.trafficLightId = item["id"].get<std::string>();
|
||||||
signal.status = [&]() {
|
signal.ns_status = [&]() {
|
||||||
int state = item["state"].get<int>();
|
int state = item["ns_status"].get<int>();
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 0: return SignalStatus::RED;
|
case 0: return SignalStatus::RED;
|
||||||
case 1: return SignalStatus::GREEN;
|
case 1: return SignalStatus::GREEN;
|
||||||
|
|||||||
@ -12,16 +12,10 @@ enum class SignalStatus {
|
|||||||
|
|
||||||
struct TrafficLightSignal {
|
struct TrafficLightSignal {
|
||||||
std::string trafficLightId; // 红绿灯设备 ID
|
std::string trafficLightId; // 红绿灯设备 ID
|
||||||
SignalStatus status; // 信号灯状态
|
SignalStatus ns_status = SignalStatus::UNKNOWN; // 南北向状态
|
||||||
|
SignalStatus ew_status = SignalStatus::UNKNOWN; // 东西向状态
|
||||||
uint64_t timestamp; // 时间戳
|
uint64_t timestamp; // 时间戳
|
||||||
std::string intersectionId; // 路口编号
|
std::string intersectionId; // 路口编号
|
||||||
double latitude; // 路口纬度
|
double latitude; // 路口纬度
|
||||||
double longitude; // 路口经度
|
double longitude; // 路口经度
|
||||||
|
|
||||||
static SignalStatus parseStatus(const std::string& status) {
|
|
||||||
if (status == "red") return SignalStatus::RED;
|
|
||||||
if (status == "green") return SignalStatus::GREEN;
|
|
||||||
if (status == "yellow") return SignalStatus::YELLOW;
|
|
||||||
return SignalStatus::UNKNOWN;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
@ -118,7 +118,8 @@ protected:
|
|||||||
TrafficLightSignal createTestTrafficLight(const std::string& id, int state) {
|
TrafficLightSignal createTestTrafficLight(const std::string& id, int state) {
|
||||||
TrafficLightSignal signal;
|
TrafficLightSignal signal;
|
||||||
signal.trafficLightId = id;
|
signal.trafficLightId = id;
|
||||||
signal.status = static_cast<SignalStatus>(state);
|
signal.ns_status = static_cast<SignalStatus>(state);
|
||||||
|
signal.ew_status = static_cast<SignalStatus>(state);
|
||||||
signal.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
signal.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
return signal;
|
return signal;
|
||||||
@ -379,9 +380,9 @@ TEST_F(DataCollectorTest, TrafficLightSignalsTest) {
|
|||||||
EXPECT_EQ(signals.size(), 3);
|
EXPECT_EQ(signals.size(), 3);
|
||||||
if (signals.size() >= 3) {
|
if (signals.size() >= 3) {
|
||||||
EXPECT_EQ(signals[0].trafficLightId, "TL001");
|
EXPECT_EQ(signals[0].trafficLightId, "TL001");
|
||||||
EXPECT_EQ(signals[0].status, SignalStatus::RED); // RED = 0
|
EXPECT_EQ(signals[0].ns_status, SignalStatus::RED); // RED = 0
|
||||||
EXPECT_EQ(signals[1].trafficLightId, "TL002");
|
EXPECT_EQ(signals[1].trafficLightId, "TL002");
|
||||||
EXPECT_EQ(signals[1].status, SignalStatus::GREEN); // GREEN = 1
|
EXPECT_EQ(signals[1].ns_status, SignalStatus::GREEN); // GREEN = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 停止数据采集
|
// 停止数据采集
|
||||||
|
|||||||
@ -76,7 +76,8 @@ protected:
|
|||||||
TrafficLightSignal createTestSignal(const std::string& id, SignalStatus status) {
|
TrafficLightSignal createTestSignal(const std::string& id, SignalStatus status) {
|
||||||
TrafficLightSignal signal;
|
TrafficLightSignal signal;
|
||||||
signal.trafficLightId = id;
|
signal.trafficLightId = id;
|
||||||
signal.status = status;
|
signal.ns_status = status;
|
||||||
|
signal.ew_status = status;
|
||||||
signal.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
|
signal.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
|
||||||
return signal;
|
return signal;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>机场车辆监控</title>
|
<title>机场车辆监控</title>
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
|
<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>
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
<script src="https://unpkg.com/leaflet-rotatedmarker/leaflet.rotatedMarker.js"></script>
|
<script src="https://unpkg.com/leaflet-rotatedmarker/leaflet.rotatedMarker.js"></script>
|
||||||
<style>
|
<style>
|
||||||
@ -11,15 +12,18 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#map {
|
#map {
|
||||||
height: 800px;
|
height: 800px;
|
||||||
width: 60%;
|
width: 60%;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#messages {
|
#messages {
|
||||||
width: 40%;
|
width: 40%;
|
||||||
height: 800px;
|
height: 800px;
|
||||||
@ -28,15 +32,35 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
.error { color: red; }
|
|
||||||
.success { color: green; }
|
.error {
|
||||||
.info { color: blue; }
|
color: red;
|
||||||
.position { color: #666; }
|
}
|
||||||
.warning { color: #f90; }
|
|
||||||
.command { color: #800080; }
|
.success {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: #f90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command {
|
||||||
|
color: #800080;
|
||||||
|
}
|
||||||
|
|
||||||
.vehicle-icon {
|
.vehicle-icon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -44,6 +68,7 @@
|
|||||||
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
||||||
border: 2px solid white;
|
border: 2px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aircraft-icon {
|
.aircraft-icon {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
@ -52,6 +77,7 @@
|
|||||||
border: 2px solid white;
|
border: 2px solid white;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aircraft-icon::after {
|
.aircraft-icon::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -63,6 +89,7 @@
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.special-vehicle-icon {
|
.special-vehicle-icon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -70,6 +97,7 @@
|
|||||||
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
||||||
border: 2px solid white;
|
border: 2px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.intersection-icon {
|
.intersection-icon {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
@ -77,6 +105,7 @@
|
|||||||
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%);
|
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;
|
border: 2px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.traffic-light {
|
.traffic-light {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
@ -84,15 +113,19 @@
|
|||||||
border: 2px solid white;
|
border: 2px solid white;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.traffic-light-red {
|
.traffic-light-red {
|
||||||
background-color: red;
|
background-color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.traffic-light-green {
|
.traffic-light-green {
|
||||||
background-color: green;
|
background-color: green;
|
||||||
}
|
}
|
||||||
|
|
||||||
.traffic-light-yellow {
|
.traffic-light-yellow {
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown-label {
|
.countdown-label {
|
||||||
background: #000;
|
background: #000;
|
||||||
border: 1px solid #666;
|
border: 1px solid #666;
|
||||||
@ -105,21 +138,25 @@
|
|||||||
padding: 1px 2px;
|
padding: 1px 2px;
|
||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
min-width: 28px;
|
min-width: 28px;
|
||||||
box-shadow: 0 0 2px rgba(0,0,0,0.5);
|
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown-red {
|
.countdown-red {
|
||||||
color: #ff3333;
|
color: #ff3333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown-green {
|
.countdown-green {
|
||||||
color: #33ff33;
|
color: #33ff33;
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown-yellow {
|
.countdown-yellow {
|
||||||
color: #ffff33;
|
color: #ffff33;
|
||||||
}
|
}
|
||||||
|
|
||||||
.distance-label {
|
.distance-label {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
@ -128,6 +165,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.command-text {
|
.command-text {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -143,23 +181,28 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.safety-border {
|
.safety-border {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: 2px solid;
|
border: 2px solid;
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emergency-border {
|
.emergency-border {
|
||||||
border-color: rgba(255, 0, 0, 0.8);
|
border-color: rgba(255, 0, 0, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.core-border {
|
.core-border {
|
||||||
border-color: rgba(255, 165, 0, 0.6);
|
border-color: rgba(255, 165, 0, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning-border {
|
.warning-border {
|
||||||
border-color: rgba(255, 255, 0, 0.4);
|
border-color: rgba(255, 255, 0, 0.4);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h2>机场车辆监控系统</h2>
|
<h2>机场车辆监控系统</h2>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -175,7 +218,7 @@
|
|||||||
<script>
|
<script>
|
||||||
let ws = null;
|
let ws = null;
|
||||||
const messagesDiv = document.getElementById('messages');
|
const messagesDiv = document.getElementById('messages');
|
||||||
|
|
||||||
// 初始化地图
|
// 初始化地图
|
||||||
const map = L.map('map').setView([36.35305878, 120.08558121], 17);
|
const map = L.map('map').setView([36.35305878, 120.08558121], 17);
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
@ -231,7 +274,7 @@
|
|||||||
|
|
||||||
// 存储所有标记
|
// 存储所有标记
|
||||||
const markers = new Map();
|
const markers = new Map();
|
||||||
const trafficLights = new Map();
|
const intersectionMarkers = new Map();
|
||||||
const intersections = new Map();
|
const intersections = new Map();
|
||||||
|
|
||||||
// 创建自定义图标
|
// 创建自定义图标
|
||||||
@ -246,7 +289,7 @@
|
|||||||
} else {
|
} else {
|
||||||
size = [20, 20]; // 其他图标保持原样
|
size = [20, 20]; // 其他图标保持原样
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有指令,创建带指令的图标
|
// 如果有指令,创建带指令的图标
|
||||||
if (command && className === 'vehicle-icon') {
|
if (command && className === 'vehicle-icon') {
|
||||||
const html = `
|
const html = `
|
||||||
@ -258,15 +301,15 @@
|
|||||||
html: html,
|
html: html,
|
||||||
className: '',
|
className: '',
|
||||||
iconSize: size,
|
iconSize: size,
|
||||||
iconAnchor: [size[0]/2, size[1]/2]
|
iconAnchor: [size[0] / 2, size[1] / 2]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 没有指令时,创建普通图标
|
// 没有指令时,创建普通图标
|
||||||
return L.divIcon({
|
return L.divIcon({
|
||||||
className: className,
|
className: className,
|
||||||
iconSize: size,
|
iconSize: size,
|
||||||
iconAnchor: [size[0]/2, size[1]/2]
|
iconAnchor: [size[0] / 2, size[1] / 2]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,29 +330,29 @@
|
|||||||
const id = data.object_id;
|
const id = data.object_id;
|
||||||
const position = [data.position.latitude, data.position.longitude];
|
const position = [data.position.latitude, data.position.longitude];
|
||||||
let iconClass;
|
let iconClass;
|
||||||
|
|
||||||
// 根据ID前缀确定图标类型
|
// 根据ID前缀确定图标类型
|
||||||
if (data.object_type === 'aircraft') {
|
if (data.object_type === 'aircraft') {
|
||||||
iconClass = 'aircraft-icon';
|
iconClass = 'aircraft-icon';
|
||||||
|
|
||||||
// 创建或更新安全边框
|
// 创建或更新安全边框
|
||||||
const borders = [
|
const borders = [
|
||||||
{size: 250, class: 'warning-border'}, // 外围预警区
|
{ size: 250, class: 'warning-border' }, // 外围预警区
|
||||||
{size: 150, class: 'core-border'}, // 核心安全区
|
{ size: 150, class: 'core-border' }, // 核心安全区
|
||||||
{size: 100, class: 'emergency-border'} // 紧急制动区
|
{ size: 100, class: 'emergency-border' } // 紧急制动区
|
||||||
];
|
];
|
||||||
|
|
||||||
borders.forEach(border => {
|
borders.forEach(border => {
|
||||||
const borderId = id + '_' + border.class;
|
const borderId = id + '_' + border.class;
|
||||||
let borderMarker = markers.get(borderId);
|
let borderMarker = markers.get(borderId);
|
||||||
|
|
||||||
if (!borderMarker) {
|
if (!borderMarker) {
|
||||||
// 创建边框标记
|
// 创建边框标记
|
||||||
borderMarker = L.marker(position, {
|
borderMarker = L.marker(position, {
|
||||||
icon: L.divIcon({
|
icon: L.divIcon({
|
||||||
className: 'safety-border ' + border.class,
|
className: 'safety-border ' + border.class,
|
||||||
iconSize: [border.size, border.size],
|
iconSize: [border.size, border.size],
|
||||||
iconAnchor: [border.size/2, border.size/2]
|
iconAnchor: [border.size / 2, border.size / 2]
|
||||||
}),
|
}),
|
||||||
rotationAngle: data.heading || 0,
|
rotationAngle: data.heading || 0,
|
||||||
rotationOrigin: 'center center'
|
rotationOrigin: 'center center'
|
||||||
@ -349,90 +392,52 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 添加红绿灯状态和倒计时变量
|
// 添加红绿灯状态和倒计时变量
|
||||||
let lastTrafficLightState = null;
|
// let lastTrafficLightState = null;
|
||||||
let countdownInterval = null;
|
// let countdownInterval = null;
|
||||||
let countdown = 10; // 10秒倒计时
|
// let countdown = 10;
|
||||||
|
|
||||||
function updateTrafficLight(data) {
|
// New function to update intersection traffic lights
|
||||||
const id = data.id;
|
function updateIntersectionTrafficLights(data) {
|
||||||
|
const intersectionId = data.intersection_id;
|
||||||
const position = [data.position.latitude, data.position.longitude];
|
const position = [data.position.latitude, data.position.longitude];
|
||||||
const state = data.status === 0 ? 'red' : data.status === 1 ? 'green' : 'yellow';
|
const nsStatus = data.ns_status; // 0: red, 1: green, 2: yellow
|
||||||
|
const ewStatus = data.ew_status; // 0: red, 1: green, 2: yellow
|
||||||
|
|
||||||
// 检查是否是西路口(TL001)的红绿灯状态变化
|
const nsColor = nsStatus === 0 ? 'red' : nsStatus === 1 ? 'green' : 'yellow';
|
||||||
if (id === 'TL001' && lastTrafficLightState !== state) {
|
const ewColor = ewStatus === 0 ? 'red' : ewStatus === 1 ? 'green' : 'yellow';
|
||||||
lastTrafficLightState = state;
|
|
||||||
// 重置倒计时
|
|
||||||
countdown = 10;
|
|
||||||
// 清除现有的倒计时
|
|
||||||
if (countdownInterval) {
|
|
||||||
clearInterval(countdownInterval);
|
|
||||||
}
|
|
||||||
// 启动新的倒计时
|
|
||||||
countdownInterval = setInterval(() => {
|
|
||||||
countdown = Math.max(0, countdown - 1);
|
|
||||||
// 更新红绿灯标签显示
|
|
||||||
const light = trafficLights.get(id);
|
|
||||||
if (light) {
|
|
||||||
// 格式化倒计时为 0:SS 格式
|
|
||||||
const countdownStr = `0:${countdown.toString().padStart(2, '0')}`;
|
|
||||||
const label = L.divIcon({
|
|
||||||
className: `countdown-label countdown-${state}`,
|
|
||||||
html: countdownStr,
|
|
||||||
iconSize: [32, 16],
|
|
||||||
iconAnchor: [16, 30]
|
|
||||||
});
|
|
||||||
// 更新或创建倒计时标签
|
|
||||||
if (!light.countdownMarker) {
|
|
||||||
light.countdownMarker = L.marker(position, {
|
|
||||||
icon: label,
|
|
||||||
interactive: false
|
|
||||||
}).addTo(map);
|
|
||||||
} else {
|
|
||||||
light.countdownMarker.setIcon(label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
let light = trafficLights.get(id);
|
// Calculate offset positions for NS and EW lights
|
||||||
if (!light) {
|
const nsPositionOffset = [position[0] + 0.00003, position[1]]; // Slightly north
|
||||||
light = L.marker(position, {
|
const ewPositionOffset = [position[0], position[1] + 0.00003]; // Slightly east
|
||||||
icon: createIcon(`traffic-light traffic-light-${state}`)
|
|
||||||
}).addTo(map);
|
let currentMarkers = intersectionMarkers.get(intersectionId);
|
||||||
// 为西路口添加倒计时显示
|
if (!currentMarkers) {
|
||||||
if (id === 'TL001') {
|
// Create new marker group
|
||||||
const countdownStr = `0:${countdown.toString().padStart(2, '0')}`;
|
currentMarkers = {};
|
||||||
const label = L.divIcon({
|
currentMarkers.nsMarker = L.marker(nsPositionOffset, {
|
||||||
className: `countdown-label countdown-${state}`,
|
icon: createIcon(`traffic-light traffic-light-${nsColor}`),
|
||||||
html: countdownStr,
|
pane: 'markerPane' // Ensure lights are above roads
|
||||||
iconSize: [32, 16],
|
}).addTo(map).bindTooltip(`Intersection ${intersectionId} (NS)`);
|
||||||
iconAnchor: [16, 30]
|
|
||||||
});
|
currentMarkers.ewMarker = L.marker(ewPositionOffset, {
|
||||||
light.countdownMarker = L.marker(position, {
|
icon: createIcon(`traffic-light traffic-light-${ewColor}`),
|
||||||
icon: label,
|
pane: 'markerPane'
|
||||||
interactive: false
|
}).addTo(map).bindTooltip(`Intersection ${intersectionId} (EW)`);
|
||||||
}).addTo(map);
|
|
||||||
}
|
intersectionMarkers.set(intersectionId, currentMarkers);
|
||||||
trafficLights.set(id, light);
|
|
||||||
} else {
|
} else {
|
||||||
light.setIcon(createIcon(`traffic-light traffic-light-${state}`));
|
// Update existing markers
|
||||||
// 更新西路口的倒计时显示
|
currentMarkers.nsMarker.setLatLng(nsPositionOffset);
|
||||||
if (id === 'TL001' && light.countdownMarker) {
|
currentMarkers.nsMarker.setIcon(createIcon(`traffic-light traffic-light-${nsColor}`));
|
||||||
const countdownStr = `0:${countdown.toString().padStart(2, '0')}`;
|
|
||||||
const label = L.divIcon({
|
currentMarkers.ewMarker.setLatLng(ewPositionOffset);
|
||||||
className: `countdown-label countdown-${state}`,
|
currentMarkers.ewMarker.setIcon(createIcon(`traffic-light traffic-light-${ewColor}`));
|
||||||
html: countdownStr,
|
|
||||||
iconSize: [32, 16],
|
|
||||||
iconAnchor: [16, 30]
|
|
||||||
});
|
|
||||||
light.countdownMarker.setIcon(label);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateVehicleCommand(vehicleId, commandType) {
|
function updateVehicleCommand(vehicleId, commandType) {
|
||||||
console.log('更新车辆指令:', vehicleId, commandType);
|
console.log('更新车辆指令:', vehicleId, commandType);
|
||||||
|
|
||||||
// 只处理无人车
|
// 只处理无人车
|
||||||
if (!vehicleId.startsWith('QN')) {
|
if (!vehicleId.startsWith('QN')) {
|
||||||
return;
|
return;
|
||||||
@ -446,7 +451,7 @@
|
|||||||
|
|
||||||
// 获取指令字母
|
// 获取指令字母
|
||||||
let commandText = '';
|
let commandText = '';
|
||||||
switch(commandType) {
|
switch (commandType) {
|
||||||
case 'ALERT':
|
case 'ALERT':
|
||||||
commandText = 'A';
|
commandText = 'A';
|
||||||
break;
|
break;
|
||||||
@ -501,23 +506,26 @@
|
|||||||
if (data.type === 'heartbeat') {
|
if (data.type === 'heartbeat') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let type = 'info'; // 默认类型
|
let type = 'info'; // 默认类型
|
||||||
let message = '';
|
let message = '';
|
||||||
|
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case 'position_update':
|
case 'position_update':
|
||||||
type = 'position';
|
type = 'position';
|
||||||
updatePosition(data);
|
updatePosition(data);
|
||||||
message = `位置更新: ${data.object_id} (${data.object_type})\n` +
|
message = `位置更新: ${data.object_id} (${data.object_type})\n` +
|
||||||
`位置: (${data.position.longitude.toFixed(6)}, ${data.position.latitude.toFixed(6)})\n` +
|
`位置: (${data.position.longitude.toFixed(6)}, ${data.position.latitude.toFixed(6)})\n` +
|
||||||
`航向: ${data.heading !== undefined ? data.heading.toFixed(2) + '°' : 'N/A'}\n` +
|
`航向: ${data.heading !== undefined ? data.heading.toFixed(2) + '°' : 'N/A'}\n` +
|
||||||
`速度: ${data.speed !== undefined ? data.speed.toFixed(2) + ' m/s' : 'N/A'}`;
|
`速度: ${data.speed !== undefined ? data.speed.toFixed(2) + ' m/s' : 'N/A'}`;
|
||||||
break;
|
break;
|
||||||
case 'traffic_light_status':
|
case 'intersection_traffic_light_status':
|
||||||
type = 'info';
|
type = 'info';
|
||||||
updateTrafficLight(data);
|
updateIntersectionTrafficLights(data);
|
||||||
message = `红绿灯状态更新:\n信号灯: ${data.id}\n状态: ${data.status === 0 ? '红灯' : data.status === 1 ? '绿灯' : '黄灯'}`;
|
message = `路口交通灯状态更新:\n` +
|
||||||
|
`路口 ID: ${data.intersection_id}\n` +
|
||||||
|
`南北状态: ${data.ns_status === 0 ? '红' : data.ns_status === 1 ? '绿' : '黄'}\n` +
|
||||||
|
`东西状态: ${data.ew_status === 0 ? '红' : data.ew_status === 1 ? '绿' : '黄'}`;
|
||||||
break;
|
break;
|
||||||
case 'collision_warning':
|
case 'collision_warning':
|
||||||
type = 'warning';
|
type = 'warning';
|
||||||
@ -544,17 +552,17 @@
|
|||||||
'PARKING_SIDE': '安全停靠'
|
'PARKING_SIDE': '安全停靠'
|
||||||
};
|
};
|
||||||
message = `收到车辆控制指令:\n车辆: ${data.vehicleId}\n` +
|
message = `收到车辆控制指令:\n车辆: ${data.vehicleId}\n` +
|
||||||
`指令类型: ${commandTypes[data.commandType] || data.commandType}\n` +
|
`指令类型: ${commandTypes[data.commandType] || data.commandType}\n` +
|
||||||
`原因: ${reasons[data.reason] || data.reason}\n` +
|
`原因: ${reasons[data.reason] || data.reason}\n` +
|
||||||
(data.targetLatitude !== undefined ? `目标位置: (${data.targetLatitude}, ${data.targetLongitude})\n` : '') +
|
(data.targetLatitude !== undefined ? `目标位置: (${data.targetLatitude}, ${data.targetLongitude})\n` : '') +
|
||||||
(data.signalState ? `信号灯状态: ${data.signalState}\n` : '') +
|
(data.signalState ? `信号灯状态: ${data.signalState}\n` : '') +
|
||||||
(data.intersectionId ? `路口ID: ${data.intersectionId}\n` : '') +
|
(data.intersectionId ? `路口ID: ${data.intersectionId}\n` : '') +
|
||||||
`时间戳: ${new Date(data.timestamp/1000000).toLocaleString()}`;
|
`时间戳: ${new Date(data.timestamp / 1000000).toLocaleString()}`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
message = '收到未知类型消息:\n' + JSON.stringify(data, null, 2);
|
message = '收到未知类型消息:\n' + JSON.stringify(data, null, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
log(message, type);
|
log(message, type);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('消息解析错误: ' + e.message, 'error');
|
log('消息解析错误: ' + e.message, 'error');
|
||||||
@ -572,32 +580,40 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 清除倒计时
|
// 清除倒计时
|
||||||
if (countdownInterval) {
|
// if (countdownInterval) {
|
||||||
clearInterval(countdownInterval);
|
// clearInterval(countdownInterval);
|
||||||
countdownInterval = null;
|
// countdownInterval = null;
|
||||||
}
|
// }
|
||||||
lastTrafficLightState = null;
|
// lastTrafficLightState = null;
|
||||||
countdown = 10;
|
// countdown = 10;
|
||||||
|
|
||||||
// 清除倒计时标记
|
// 清除倒计时标记
|
||||||
trafficLights.forEach(light => {
|
// trafficLights.forEach(light => {
|
||||||
if (light.countdownMarker) {
|
// if (light.countdownMarker) {
|
||||||
map.removeLayer(light.countdownMarker);
|
// map.removeLayer(light.countdownMarker);
|
||||||
light.countdownMarker = null;
|
// light.countdownMarker = null;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
ws.close();
|
ws.close();
|
||||||
ws = null;
|
ws = null;
|
||||||
|
|
||||||
// 清除所有标记(包括边框标记)
|
// 清除所有车辆和安全边框标记
|
||||||
markers.forEach((marker, key) => {
|
markers.forEach((marker, key) => {
|
||||||
map.removeLayer(marker);
|
map.removeLayer(marker);
|
||||||
});
|
});
|
||||||
markers.clear();
|
markers.clear();
|
||||||
|
|
||||||
trafficLights.forEach(light => map.removeLayer(light));
|
// 清除旧的红绿灯标记
|
||||||
trafficLights.clear();
|
// trafficLights.forEach(light => map.removeLayer(light));
|
||||||
|
// trafficLights.clear();
|
||||||
|
|
||||||
|
// 清除路口红绿灯标记
|
||||||
|
intersectionMarkers.forEach((markers, id) => {
|
||||||
|
if (markers.nsMarker) map.removeLayer(markers.nsMarker);
|
||||||
|
if (markers.ewMarker) map.removeLayer(markers.ewMarker);
|
||||||
|
});
|
||||||
|
intersectionMarkers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加道路刻度标记函数
|
// 添加道路刻度标记函数
|
||||||
@ -607,26 +623,26 @@
|
|||||||
const lon1 = startPoint[1];
|
const lon1 = startPoint[1];
|
||||||
const lat2 = endPoint[0];
|
const lat2 = endPoint[0];
|
||||||
const lon2 = endPoint[1];
|
const lon2 = endPoint[1];
|
||||||
|
|
||||||
// 计算道路角度(考虑经纬度投影)
|
// 计算道路角度(考虑经纬度投影)
|
||||||
const latMid = (lat1 + lat2) / 2; // 使用中点纬度来算经度缩放
|
const latMid = (lat1 + lat2) / 2; // 使用中点纬度来算经度缩放
|
||||||
const lonScale = Math.cos(latMid * Math.PI / 180); // 经度缩放因子
|
const lonScale = Math.cos(latMid * Math.PI / 180); // 经度缩放因子
|
||||||
const dx = (lon2 - lon1) * lonScale;
|
const dx = (lon2 - lon1) * lonScale;
|
||||||
const dy = lat2 - lat1;
|
const dy = lat2 - lat1;
|
||||||
const angle = Math.atan2(dy, dx);
|
const angle = Math.atan2(dy, dx);
|
||||||
|
|
||||||
// 计算垂直于道路的方向(只在右侧显示刻度)
|
// 计算垂直于道路的方向(只在右侧显示刻度)
|
||||||
const perpAngle = angle + Math.PI / 2;
|
const perpAngle = angle + Math.PI / 2;
|
||||||
const markLength = 0.00005; // 保持您设置的较短刻度线长度
|
const markLength = 0.00005; // 保持您设置的较短刻度线长度
|
||||||
const offset = 0.00004; // 向右偏移一点,避免与道路重叠
|
const offset = 0.00004; // 向右偏移一点,避免与道路重叠
|
||||||
|
|
||||||
// 计算总距离
|
// 计算总距离
|
||||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
// 每50米一个刻度
|
// 每50米一个刻度
|
||||||
const step = 0.0005; // 约50米
|
const step = 0.0005; // 约50米
|
||||||
const steps = Math.floor(dist / step);
|
const steps = Math.floor(dist / step);
|
||||||
|
|
||||||
for (let i = 0; i <= steps; i++) {
|
for (let i = 0; i <= steps; i++) {
|
||||||
// 计算刻度位置
|
// 计算刻度位置
|
||||||
const ratio = i / steps;
|
const ratio = i / steps;
|
||||||
@ -635,25 +651,25 @@
|
|||||||
lat1 + dy * ratio,
|
lat1 + dy * ratio,
|
||||||
lon1 + (dx / lonScale) * ratio
|
lon1 + (dx / lonScale) * ratio
|
||||||
];
|
];
|
||||||
|
|
||||||
// 计算垂直偏移(考虑经纬度投影)
|
// 计算垂直偏移(考虑经纬度投影)
|
||||||
const offsetPos = [
|
const offsetPos = [
|
||||||
pos[0] + Math.sin(perpAngle) * offset,
|
pos[0] + Math.sin(perpAngle) * offset,
|
||||||
pos[1] + Math.cos(perpAngle) * offset / lonScale
|
pos[1] + Math.cos(perpAngle) * offset / lonScale
|
||||||
];
|
];
|
||||||
|
|
||||||
// 计算刻度线终点(考虑经纬度投影)
|
// 计算刻度线终点(考虑经纬度投影)
|
||||||
const markEnd = [
|
const markEnd = [
|
||||||
offsetPos[0] + Math.sin(perpAngle) * markLength,
|
offsetPos[0] + Math.sin(perpAngle) * markLength,
|
||||||
offsetPos[1] + Math.cos(perpAngle) * markLength / lonScale
|
offsetPos[1] + Math.cos(perpAngle) * markLength / lonScale
|
||||||
];
|
];
|
||||||
|
|
||||||
// 添加刻度线
|
// 添加刻度线
|
||||||
L.polyline([offsetPos, markEnd], {
|
L.polyline([offsetPos, markEnd], {
|
||||||
color: '#666',
|
color: '#666',
|
||||||
weight: 1.5
|
weight: 1.5
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
// 添加距离标签
|
// 添加距离标签
|
||||||
const distance = Math.round(i * 50);
|
const distance = Math.round(i * 50);
|
||||||
if (distance > 0) {
|
if (distance > 0) {
|
||||||
@ -663,7 +679,7 @@
|
|||||||
iconSize: [40, 20],
|
iconSize: [40, 20],
|
||||||
iconAnchor: [-5, 10]
|
iconAnchor: [-5, 10]
|
||||||
});
|
});
|
||||||
|
|
||||||
L.marker(markEnd, {
|
L.marker(markEnd, {
|
||||||
icon: label,
|
icon: label,
|
||||||
interactive: false
|
interactive: false
|
||||||
@ -720,4 +736,5 @@
|
|||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
|
||||||
|
</html>
|
||||||
Loading…
Reference in New Issue
Block a user