From 7a164e24123df74c3228324e4a398b79cb9c12b6 Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: Fri, 12 Dec 2025 14:37:17 +0800
Subject: [PATCH] 增加禁飞区距离计算

---
 ard-work/src/main/java/com/ard/work/component/websocket/handler/NoFlyDistanceManager.java |   76 +++++++++++++++++++
 ard-work/src/main/java/com/ard/work/domain/dto/NoFlyDistanceDTO.java                      |   18 ++++
 ard-work/src/main/java/com/ard/work/domain/message/NoFlyDistanceMessage.java              |   30 +++++++
 ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java     |   36 +++++++++
 ard-work/src/main/java/com/ard/work/domain/enums/MessageType.java                         |    1 
 ard-system/src/main/java/com/ard/data/noflyzone/utils/NoFlyZoneUtils.java                 |   78 +++++++++++++++++++
 6 files changed, 239 insertions(+), 0 deletions(-)

diff --git a/ard-system/src/main/java/com/ard/data/noflyzone/utils/NoFlyZoneUtils.java b/ard-system/src/main/java/com/ard/data/noflyzone/utils/NoFlyZoneUtils.java
index ea09a5a..e37cba3 100644
--- a/ard-system/src/main/java/com/ard/data/noflyzone/utils/NoFlyZoneUtils.java
+++ b/ard-system/src/main/java/com/ard/data/noflyzone/utils/NoFlyZoneUtils.java
@@ -15,6 +15,72 @@
 
 public class NoFlyZoneUtils {
 
+
+    /**
+     * 计算点到多边形的最近距离
+     */
+    public static double pointToPolygonDistance(Vertex point, List<Vertex> polygon) {
+
+        // 如果在多边形内部,距离为 0
+        if (isPointInPolygon(point, polygon)) {
+            return 0;
+        }
+
+        double minDistance = Double.MAX_VALUE;
+        int n = polygon.size();
+
+        for (int i = 0; i < n; i++) {
+            Vertex a = polygon.get(i);
+            Vertex b = polygon.get((i + 1) % n); // 下一点,最后一条边连接第一个点
+
+            double dist = pointToSegmentDistance(point, a, b);
+            minDistance = Math.min(minDistance, dist);
+        }
+
+        return minDistance;
+    }
+
+    /**
+     * 计算点到线段的距离
+     */
+    public static double pointToSegmentDistance(Vertex p, Vertex a, Vertex b) {
+        double x = p.x, y = p.y;
+        double x1 = a.x, y1 = a.y;
+        double x2 = b.x, y2 = b.y;
+
+        double dx = x2 - x1;
+        double dy = y2 - y1;
+
+        if (dx == 0 && dy == 0) {
+            // A 和 B 是同一个点
+            dx = x - x1;
+            dy = y - y1;
+            return Math.sqrt(dx * dx + dy * dy);
+        }
+
+        // 投影参数 t(0-1之间表示在线段内部)
+        double t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
+
+        if (t < 0) {
+            // 最近点是 A
+            dx = x - x1;
+            dy = y - y1;
+        } else if (t > 1) {
+            // 最近点是 B
+            dx = x - x2;
+            dy = y - y2;
+        } else {
+            // 最近点在线段中间
+            double projX = x1 + t * dx;
+            double projY = y1 + t * dy;
+            dx = x - projX;
+            dy = y - projY;
+        }
+
+        return Math.sqrt(dx * dx + dy * dy);
+    }
+
+
     /**
      * 判断点是否在多边形内(射线法)
      */
@@ -80,6 +146,18 @@
 
 
     /**
+     * 判断点是否在矩形内
+     */
+    public static boolean isInsideBox(Vertex point, ArdNoFlyBox box) {
+        if (point == null || box == null) return false;
+        return point.x >= box.getMinx() &&
+                point.x <= box.getMaxx() &&
+                point.y >= box.getMiny() &&
+                point.y <= box.getMaxy();
+    }
+
+
+    /**
      * 根据 GeoJSON(FeatureCollection)计算外接矩形
      */
     @SuppressWarnings("unchecked")
diff --git a/ard-work/src/main/java/com/ard/work/component/websocket/handler/NoFlyDistanceManager.java b/ard-work/src/main/java/com/ard/work/component/websocket/handler/NoFlyDistanceManager.java
new file mode 100644
index 0000000..52a3a6e
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/component/websocket/handler/NoFlyDistanceManager.java
@@ -0,0 +1,76 @@
+package com.ard.work.component.websocket.handler;
+
+import com.ard.data.noflyzone.domain.ArdNoFlyBox;
+import com.ard.data.noflyzone.domain.ArdNoFlyZone;
+import com.ard.data.noflyzone.domain.Vertex;
+import com.ard.data.noflyzone.service.IArdNoFlyZoneService;
+import com.ard.data.noflyzone.utils.NoFlyZoneUtils;
+import com.ard.work.domain.cache.UavTelemetryDataCache;
+import com.ard.work.domain.dto.NoFlyDistanceDTO;
+import com.ard.work.domain.dto.telemetry.TelemetryData;
+import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 禁飞区距离管理器
+ * @author liuyaping
+ * @date 2021/3/22
+ */
+@Component
+public class NoFlyDistanceManager extends BaseSubscriptionManager<NoFlyDistanceDTO> {
+
+    @Resource
+    private IArdNoFlyZoneService ardNoFlyZoneService;
+
+    @Override
+    protected NoFlyDistanceDTO getData(String boxSn) {
+        TelemetryData telemetryData = UavTelemetryDataCache.get(boxSn);
+        if (telemetryData == null) return null;
+
+        Vertex current = new Vertex(telemetryData.getLng(), telemetryData.getLat());
+
+        List<ArdNoFlyZone> zones = ardNoFlyZoneService.selectArdNoFlyZoneList(new ArdNoFlyZone());
+        if (zones.isEmpty()) return null;
+
+        double minDistance = Double.MAX_VALUE;
+        ArdNoFlyZone nearestZone = null;
+
+        for (ArdNoFlyZone zone : zones) {
+
+            ArdNoFlyBox box = zone.getArdNoFlyBox();
+            if (box == null) continue;
+
+            // 包围盒快速排除
+            if (current.x < box.getMinx() || current.x > box.getMaxx() ||
+                    current.y < box.getMiny() || current.y > box.getMaxy()) {
+                continue;
+            }
+
+            List<Vertex> polygon = zone.getVertices();
+            if (polygon == null || polygon.size() < 3) continue;
+
+            double distance = NoFlyZoneUtils.pointToPolygonDistance(current, polygon);
+
+            if (distance < minDistance) {
+                minDistance = distance;
+                nearestZone = zone;
+            }
+        }
+
+        if (nearestZone == null) return null;
+
+        return NoFlyDistanceDTO.builder()
+                .boxSn(boxSn)
+                .zoneName(nearestZone.getName())
+                .distance(minDistance)
+                .inNoFlyZone(minDistance == 0)
+                .build();
+    }
+
+
+    @Override
+    protected String getMessageType() {
+        return "noFlyZoneDistance";
+    }
+}
diff --git a/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java b/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java
index 2d376d4..c8873a4 100644
--- a/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java
+++ b/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java
@@ -42,6 +42,8 @@
     @Resource
     private UavAllManager uavAllManager;
     @Resource
+    private NoFlyDistanceManager noFlyDistanceManager;
+    @Resource
     private TokenService tokenService;
     private final Map<Long, Long> lastDisconnectTime = new ConcurrentHashMap<>();
 
@@ -73,6 +75,9 @@
                     break;
                 case CONTROLLER:
                     handleControlMessage(payload, userId);
+                    break;
+                case NO_FLY_DISTANCE:
+                    handleNoFlyDistanceMessage(payload, userId);
                     break;
                 case ALL:
                     handleAllMessage(payload, userId);
@@ -272,6 +277,37 @@
         }
     }
 
+    /**
+     * 处理禁飞区距离
+     *
+     * @param payload 消息负载,包含订阅信息
+     * @param userId  用户ID,用于标识用户
+     */
+    private void handleNoFlyDistanceMessage(String payload, Long userId) {
+        // 解析接收到的JSON消息,将其转换为BaseMessage对象
+        BaseMessage<NoFlyDistanceMessage> baseMessage = JSON.parseObject(payload,
+                new TypeReference<BaseMessage<NoFlyDistanceMessage>>() {
+                });
+        // 获取消息的有效载荷部分
+        NoFlyDistanceMessage noFlyDistanceMessage = baseMessage.getPayload();
+
+        // 检查有效载荷是否存在
+        if (noFlyDistanceMessage != null) {
+            // 获取无人机盒子的序列号
+            String boxSn = noFlyDistanceMessage.getBoxSn();
+            // 根据消息中的订阅标志,决定是订阅还是退订无人机状态
+            Boolean subscribe = noFlyDistanceMessage.getSubscribe();
+            if (subscribe) {
+                // 执行订阅操作
+                noFlyDistanceManager.subscribe(boxSn, userId, 1);
+            } else {
+                // 执行退订操作
+                noFlyDistanceManager.unsubscribe(boxSn, userId);
+            }
+        }
+    }
+
+
     @Override
     public void afterConnectionEstablished(WebSocketSession session) {
         String userIdStr = (String) session.getAttributes().get("userId");
diff --git a/ard-work/src/main/java/com/ard/work/domain/dto/NoFlyDistanceDTO.java b/ard-work/src/main/java/com/ard/work/domain/dto/NoFlyDistanceDTO.java
new file mode 100644
index 0000000..5a54310
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/domain/dto/NoFlyDistanceDTO.java
@@ -0,0 +1,18 @@
+package com.ard.work.domain.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class NoFlyDistanceDTO {
+    private String boxSn;
+    private String zoneName;
+    private double distance; // 米
+    private boolean inNoFlyZone;
+    // getters/setters
+}
diff --git a/ard-work/src/main/java/com/ard/work/domain/enums/MessageType.java b/ard-work/src/main/java/com/ard/work/domain/enums/MessageType.java
index 1c5f5a1..d4a751a 100644
--- a/ard-work/src/main/java/com/ard/work/domain/enums/MessageType.java
+++ b/ard-work/src/main/java/com/ard/work/domain/enums/MessageType.java
@@ -7,6 +7,7 @@
     TELEMETRY("telemetry"),
     SIGNAL("signal"),
     CONTROLLER("controller"),
+    NO_FLY_DISTANCE("noFlyDistance"),
     ALL("all");
 
     private final String type;
diff --git a/ard-work/src/main/java/com/ard/work/domain/message/NoFlyDistanceMessage.java b/ard-work/src/main/java/com/ard/work/domain/message/NoFlyDistanceMessage.java
new file mode 100644
index 0000000..b7e8b20
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/domain/message/NoFlyDistanceMessage.java
@@ -0,0 +1,30 @@
+package com.ard.work.domain.message;
+
+import com.ard.work.domain.dto.signal.SignalInfo;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Description: 禁飞距离消息
+ * Author: liusuyi
+ * Date: 2025年03月10日11:49
+ **/
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class NoFlyDistanceMessage {
+
+    /**
+     * 盒子编号
+     */
+    private String boxSn;
+
+    /**
+     * 是否订阅
+     */
+    private Boolean subscribe;
+
+}

--
Gitblit v1.9.3