From 145e705946adf1e47512f2450e8bdd42662751e6 Mon Sep 17 00:00:00 2001
From: zhaowenyu <35589726@qq.com>
Date: Sat, 13 Dec 2025 09:38:26 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev' into dev

---
 ard-work/src/main/java/com/ard/work/domain/dto/NoFlyDistanceDTO.java                      |   18 ++
 ard-route/src/main/java/com/ard/route/domain/route/ArdRouteInfo.java                      |    9 +
 ard-work/src/main/java/com/ard/work/domain/enums/MessageType.java                         |    1 
 ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java           |   59 +++++++-
 ard-system/src/main/java/com/ard/data/noflyzone/utils/NoFlyZoneUtils.java                 |   89 ++++++++++++
 ard-system/src/main/resources/mapper/data/ArdTileMapper.xml                               |   30 ++--
 ard-work/src/main/java/com/ard/work/component/websocket/handler/NoFlyDistanceManager.java |  100 ++++++++++++++
 ard-route/src/main/java/com/ard/route/util/InitializationUtils.java                       |   22 ++
 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-route/src/main/java/com/ard/route/service/impl/UavRouteServiceImpl.java               |   15 ++
 ard-system/src/main/java/com/ard/data/tile/domain/ArdTile.java                            |    8 
 ard-route/src/main/java/com/ard/route/domain/vo/KmzResultVo.java                          |    3 
 13 files changed, 388 insertions(+), 32 deletions(-)

diff --git a/ard-route/src/main/java/com/ard/route/domain/route/ArdRouteInfo.java b/ard-route/src/main/java/com/ard/route/domain/route/ArdRouteInfo.java
index 8b32204..8339a2f 100644
--- a/ard-route/src/main/java/com/ard/route/domain/route/ArdRouteInfo.java
+++ b/ard-route/src/main/java/com/ard/route/domain/route/ArdRouteInfo.java
@@ -49,11 +49,16 @@
     @Excel(name = "负载类型")
     private String payload;
 
-    /** 是否只读 是否只读0:否 1:是 */
+    /**
+     * 是否只读 是否只读
+     * 0:否
+     * 1:是
+     */
     @Excel(name = "是否只读")
     private String readOnly;
 
-    /** 时间排序
+    /**
+     * 时间排序
      * 0:正序
      * 1:倒序
      */
diff --git a/ard-route/src/main/java/com/ard/route/domain/vo/KmzResultVo.java b/ard-route/src/main/java/com/ard/route/domain/vo/KmzResultVo.java
index 8ef3508..1df779f 100644
--- a/ard-route/src/main/java/com/ard/route/domain/vo/KmzResultVo.java
+++ b/ard-route/src/main/java/com/ard/route/domain/vo/KmzResultVo.java
@@ -8,6 +8,9 @@
     /** 文件路径 */
     private String path;
 
+    /** 文件路径 */
+    private String pathDock;
+
     /** 返回数据 */
     private String data;
 
diff --git a/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java b/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java
index 905d321..522afd6 100644
--- a/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java
+++ b/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java
@@ -12,6 +12,7 @@
 import com.ard.data.noflyzone.domain.Vertex;
 import com.ard.data.noflyzone.service.IArdNoFlyZoneService;
 import com.ard.data.noflyzone.utils.NoFlyZoneUtils;
+import com.ard.framework.web.service.TokenService;
 import com.ard.oss.model.MinioUploadResult;
 import com.ard.oss.service.impl.OssServiceContext;
 import com.ard.ossdock.service.impl.OssDockServiceContext;
@@ -29,12 +30,15 @@
 import com.ard.work.domain.dto.state.PointData;
 import com.ard.work.domain.dto.wayline.*;
 import com.ard.work.service.ITyService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 
+import static com.ard.route.util.InitializationUtils.divided_6;
 import static com.ard.route.util.ToolUtils.*;
 import static com.ard.route.util.UavRouteReqUtils.fileConversionEntity;
 
@@ -46,6 +50,8 @@
  */
 @Service
 public class ArdRouteInfoServiceImpl implements IArdRouteInfoService {
+
+    private static final Logger log = LoggerFactory.getLogger(TokenService.class);
 
     @Autowired
     private ArdRouteInfoMapper ardRouteInfoMapper;
@@ -83,6 +89,14 @@
         }
         for (RoutePointReq routePointReq : ardRouteInfo.getUavRouteReq().getRoutePointList()) {
             routePointReq.setHeight(routePointReq.getHeight() + getDoublePoint(ardRouteInfo.getUavRouteReq().getTakeOffRefPoint(),"alt"));
+            // 判断无人机类型
+            if (ardRouteInfo.getUavRouteReq().getDroneType().equals(89)){
+                for (ActionGroupReq actionGroupReq : routePointReq.getActionGroupList()) {
+                    for (PointActionReq action : actionGroupReq.getActions()) {
+                        action.setZoom(divided_6(action.getZoom()));
+                    }
+                }
+            }
         }
         return ardRouteInfo;
     }
@@ -154,7 +168,6 @@
             }
             ardRouteInfo.setPath(kmzResult.getPath());
         }
-        ardRouteInfo.setReadOnly("0");
         return ardRouteInfoMapper.updateArdRouteInfo(dataProcessing(ardRouteInfo));
     }
 
@@ -238,13 +251,17 @@
                 ardRouteInfo.setType(templateTypeData(uavRouteReq));
                 ardRouteInfo.setAircraft(droneTypeData(uavRouteReq));
             }
+
             // 判断禁飞区
-//            List<RoutePointReq> routePointList = ardRouteInfo.getUavRouteReq().getRoutePointList();
-//            for (RoutePointReq routePointReq : routePointList) {
-//                ArdNoflyDto ardNoflyDto = new ArdNoflyDto();
-//                ardNoflyDto.setCurrent(new Vertex(routePointReq.getLongitude(), routePointReq.getLatitude()));
-//                verifyNoFlyZone(ardNoflyDto);
-//            }
+            try {
+                arrayWithinNoFlyZone(ardRouteInfo.getUavRouteReq().getRoutePointList());
+            } catch (ControlException e) {
+                // 返回包含禁飞区错误信息的特定结果
+                return MinioUploadResult.builder()
+                        .originalFilename("禁飞区错误.航线航点在禁飞区!")
+                        .build();
+            }
+
             String url = ossService.upload("kmz",file);
             String urlDock = ossDockService.upload("kmz",file);
             ardRouteInfo.setName(file.getOriginalFilename());
@@ -259,11 +276,39 @@
                     .originalFilename(file.getOriginalFilename())
                     .build();
         }catch (Exception e){
+            log.error("上传航线文件失败", e);
             return null;
         }
     }
 
     /**
+     * 判断一组数组,是否包含禁飞区
+     */
+    private void arrayWithinNoFlyZone(List<RoutePointReq> routePointList) {
+        if (routePointList == null || routePointList.isEmpty()) {
+            return;
+        }
+        // 检查第一个航点
+        ArdNoflyDto firstDto = new ArdNoflyDto();
+        firstDto.setTarget(new Vertex(
+                routePointList.get(0).getLongitude(),
+                routePointList.get(0).getLatitude()));
+        verifyNoFlyZone(firstDto);
+
+        // 检查后续航点及与前一个航点的线段
+        for (int i = 1; i < routePointList.size(); i++) {
+            ArdNoflyDto dto = new ArdNoflyDto();
+            dto.setCurrent(new Vertex(
+                    routePointList.get(i - 1).getLongitude(),
+                    routePointList.get(i - 1).getLatitude()));
+            dto.setTarget(new Vertex(
+                    routePointList.get(i).getLongitude(),
+                    routePointList.get(i).getLatitude()));
+            verifyNoFlyZone(dto);
+        }
+    }
+
+    /**
      * 根据名称查询航线信息
      *
      * @param name 文件名称
diff --git a/ard-route/src/main/java/com/ard/route/service/impl/UavRouteServiceImpl.java b/ard-route/src/main/java/com/ard/route/service/impl/UavRouteServiceImpl.java
index 10f613c..648be05 100644
--- a/ard-route/src/main/java/com/ard/route/service/impl/UavRouteServiceImpl.java
+++ b/ard-route/src/main/java/com/ard/route/service/impl/UavRouteServiceImpl.java
@@ -3,6 +3,7 @@
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import com.ard.oss.service.impl.OssServiceContext;
+import com.ard.ossdock.service.impl.OssDockServiceContext;
 import com.ard.route.constant.FileTypeConstants;
 import com.ard.route.domain.djuav.RoutePointReq;
 import com.ard.route.domain.djuav.UavRouteReq;
@@ -24,6 +25,7 @@
 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.mock.web.MockMultipartFile;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
@@ -48,6 +50,9 @@
 
     @Resource
     private OssServiceContext ossService;
+
+    @Resource
+    private OssDockServiceContext ossDockService;
 
     @Resource
     private IArdRouteInfoService ardRouteInformationService;
@@ -87,6 +92,10 @@
                 }
             }
             // 删除文件
+            if ("100".equals(kmlInfo.getDocument().getKmlMissionConfig().getDroneInfo().getDroneEnumValue())
+                    ||"91".equals(kmlInfo.getDocument().getKmlMissionConfig().getDroneInfo().getDroneEnumValue())){
+                ossDockService.deleteObject(kmzPath,minioUrlName);
+            }
             ossService.deleteObject(kmzPath, minioUrlName);
             return buildKmzMethod(routeInformation.getName(), kmlInfo, wpmlInfo);
         } catch (Exception e) {
@@ -199,6 +208,12 @@
                     "application/vnd.google-earth.kmz",
                     new ByteArrayInputStream(baos.toByteArray())
             );
+            // 通过无人机类型,判断上传文件
+            if ("100".equals(kmlInfo.getDocument().getKmlMissionConfig().getDroneInfo().getDroneEnumValue())
+                    ||"91".equals(kmlInfo.getDocument().getKmlMissionConfig().getDroneInfo().getDroneEnumValue())){
+                String kmzFilePathDock = ossDockService.upload(kmzPath,multipartFile);
+                result.setPathDock(kmzFilePathDock);
+            }
             String kmzFilePath = ossService.upload(kmzPath,multipartFile);
             result.setPath(kmzFilePath);
             return result;
diff --git a/ard-route/src/main/java/com/ard/route/util/InitializationUtils.java b/ard-route/src/main/java/com/ard/route/util/InitializationUtils.java
index 747af86..8ef4f90 100644
--- a/ard-route/src/main/java/com/ard/route/util/InitializationUtils.java
+++ b/ard-route/src/main/java/com/ard/route/util/InitializationUtils.java
@@ -17,7 +17,7 @@
 public class InitializationUtils {
 
     // 禅思H20T 公倍数
-    private static final Double CHANSI_H20 = 23.76237623;
+    private static final Double CHANSI_H20T = 23.76237623;
 
     // 禅思H20T 焦距值
     private static final Double CHANSI_H20_FOCUS = 6.83;
@@ -101,14 +101,30 @@
             for (ActionGroupReq actionGroupReq : routePointReq.getActionGroupList()) {
                 for (PointActionReq action : actionGroupReq.getActions()) {
                     if (action.getZoom() != null){
-                        //action.setZoom(action.getZoom() * CHANSI_H20);
-                        action.setZoom(action.getZoom() * CHANSI_H20_FOCUS);
+                        action.setZoom(multiple_23(action.getZoom()));
+                        //action.setZoom(multiple_6(action.getZoom()));
                     }
                 }
             }
         }
     }
 
+    public static Double multiple_23(Double zoom){
+        return zoom * CHANSI_H20T;
+    }
+
+    public static Double multiple_6(Double zoom){
+        return zoom * CHANSI_H20_FOCUS;
+    }
+
+    public static Double divided_23(Double zoom){
+        return zoom / CHANSI_H20T;
+    }
+
+    public static Double divided_6(Double zoom){
+        return zoom / CHANSI_H20_FOCUS;
+    }
+
     /**
      * 初始化数据
      */
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..26fc38f 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,78 @@
 
 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 = pointToSegmentDistanceHaversine(point, a, b);
+            minDistance = Math.min(minDistance, dist);
+        }
+
+        return minDistance;
+    }
+
+    /**
+     * 计算点到线段的距离(米)使用Haversine公式
+     */
+    public static double pointToSegmentDistanceHaversine(Vertex p, Vertex a, Vertex b) {
+        // 将经纬度换算成以 p 点为参考的局部平面米坐标
+        double[] pa = latLonToXY(a, p.y, p.x);
+        double[] pb = latLonToXY(b, p.y, p.x);
+        double[] pp = latLonToXY(p, p.y, p.x);
+
+        double dx = pb[0] - pa[0];
+        double dy = pb[1] - pa[1];
+
+        double t = ((pp[0] - pa[0]) * dx + (pp[1] - pa[1]) * dy) / (dx * dx + dy * dy);
+
+        double nearestX, nearestY;
+        if (t < 0) {
+            nearestX = pa[0];
+            nearestY = pa[1];
+        } else if (t > 1) {
+            nearestX = pb[0];
+            nearestY = pb[1];
+        } else {
+            nearestX = pa[0] + t * dx;
+            nearestY = pa[1] + t * dy;
+        }
+
+        double distX = pp[0] - nearestX;
+        double distY = pp[1] - nearestY;
+
+        return Math.sqrt(distX * distX + distY * distY);
+    }
+
+    /**
+     * 将经纬度转换为局部平面坐标(米)
+     * originLat、originLon 是参考点,经纬度转换到以参考点为原点的平面坐标
+     */
+    public static double[] latLonToXY(Vertex point, double originLat, double originLon) {
+        double R = 6371000; // 地球半径,单位米
+        double dLat = Math.toRadians(point.y - originLat);
+        double dLon = Math.toRadians(point.x - originLon);
+        double x = R * dLon * Math.cos(Math.toRadians(originLat));
+        double y = R * dLat;
+        return new double[]{x, y};
+    }
+
+
+
     /**
      * 判断点是否在多边形内(射线法)
      */
@@ -76,6 +148,18 @@
     private static boolean onSegment(Vertex p1, Vertex p2, Vertex q) {
         return q.x <= Math.max(p1.x, p2.x) && q.x >= Math.min(p1.x, p2.x)
                 && q.y <= Math.max(p1.y, p2.y) && q.y >= Math.min(p1.y, p2.y);
+    }
+
+
+    /**
+     * 判断点是否在矩形内
+     */
+    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();
     }
 
 
@@ -166,5 +250,8 @@
         return vertexes;
     }
 
-
+    public static double degDistanceToMeter(double deg, double lat) {
+        // 1° ≈ 111320 米,但是经度和纬度不一样,这里用纬度做修正
+        return deg * 111320.0 * Math.cos(Math.toRadians(lat));
+    }
 }
diff --git a/ard-system/src/main/java/com/ard/data/tile/domain/ArdTile.java b/ard-system/src/main/java/com/ard/data/tile/domain/ArdTile.java
index b3fae81..dca23b1 100644
--- a/ard-system/src/main/java/com/ard/data/tile/domain/ArdTile.java
+++ b/ard-system/src/main/java/com/ard/data/tile/domain/ArdTile.java
@@ -34,7 +34,7 @@
 
     /** 屏幕空间误差 */
     @Excel(name = "屏幕空间误差")
-    private Double screenSpaceError;
+    private Double maximumScreenSpaceError;
 
     /** 内存最大使用量 */
     @Excel(name = "内存最大使用量")
@@ -42,11 +42,11 @@
 
     /** 自动超清化模型 */
     @Excel(name = "自动超清化模型")
-    private Double automaticSuperCleaningModel;
+    private Double cullRequestsWhileMovingMultiplier;
 
     /** 加载速度 */
-    @Excel(name = "加载速度")
-    private Double loadingSpeed;
+    @Excel(name = "模型加载速度")
+    private Double dynamicScreenSpaceErrorDensity;
 
     /** 用户ID */
     @Excel(name = "用户ID")
diff --git a/ard-system/src/main/resources/mapper/data/ArdTileMapper.xml b/ard-system/src/main/resources/mapper/data/ArdTileMapper.xml
index f0b9cb6..2ed3409 100644
--- a/ard-system/src/main/resources/mapper/data/ArdTileMapper.xml
+++ b/ard-system/src/main/resources/mapper/data/ArdTileMapper.xml
@@ -9,10 +9,10 @@
         <result property="tilesName"    column="tiles_name"    />
         <result property="tilesHeight"    column="tiles_height"    />
         <result property="tileAddress"    column="tile_address"    />
-        <result property="screenSpaceError"    column="screen_space_error"    />
+        <result property="maximumScreenSpaceError"    column="maximum_screen_space_error"    />
         <result property="maximumMemoryUsage"    column="maximum_memory_usage"    />
-        <result property="automaticSuperCleaningModel"    column="automatic_super_cleaning_model"    />
-        <result property="loadingSpeed"    column="loading_speed"    />
+        <result property="cullRequestsWhileMovingMultiplier"    column="cull_requests_while_moving_multiplier"    />
+        <result property="dynamicScreenSpaceErrorDensity"    column="dynamic_screen_space_error_density"    />
         <result property="userId"    column="user_id"    />
         <result property="deptId"    column="dept_id"    />
         <result property="defaultLoading"    column="default_loading"    />
@@ -23,10 +23,10 @@
                tiles_name,
                tiles_height,
                tile_address,
-               screen_space_error,
+               maximum_screen_space_error,
                maximum_memory_usage,
-               automatic_super_cleaning_model,
-               loading_speed,
+               cull_requests_while_moving_multiplier,
+               dynamic_screen_space_error_density,
                user_id,
                dept_id,
                default_loading
@@ -54,10 +54,10 @@
             <if test="tilesName != null">tiles_name,</if>
             <if test="tilesHeight != null">tiles_height,</if>
             <if test="tileAddress != null">tile_address,</if>
-            <if test="screenSpaceError != null">screen_space_error,</if>
+            <if test="maximumScreenSpaceError != null">maximum_screen_space_error,</if>
             <if test="maximumMemoryUsage != null">maximum_memory_usage,</if>
-            <if test="automaticSuperCleaningModel != null">automatic_super_cleaning_model,</if>
-            <if test="loadingSpeed != null">loading_speed,</if>
+            <if test="cullRequestsWhileMovingMultiplier != null">cull_requests_while_moving_multiplier,</if>
+            <if test="dynamicScreenSpaceErrorDensity != null">dynamic_screen_space_error_density,</if>
             <if test="userId != null">user_id,</if>
             <if test="deptId != null">dept_id,</if>
             <if test="defaultLoading != null">default_loading,</if>
@@ -67,10 +67,10 @@
             <if test="tilesName != null">#{tilesName},</if>
             <if test="tilesHeight != null">#{tilesHeight},</if>
             <if test="tileAddress != null">#{tileAddress},</if>
-            <if test="screenSpaceError != null">#{screenSpaceError},</if>
+            <if test="maximumScreenSpaceError != null">#{maximumScreenSpaceError},</if>
             <if test="maximumMemoryUsage != null">#{maximumMemoryUsage},</if>
-            <if test="automaticSuperCleaningModel != null">#{automaticSuperCleaningModel},</if>
-            <if test="loadingSpeed != null">#{loadingSpeed},</if>
+            <if test="cullRequestsWhileMovingMultiplier != null">#{cullRequestsWhileMovingMultiplier},</if>
+            <if test="dynamicScreenSpaceErrorDensity != null">#{dynamicScreenSpaceErrorDensity},</if>
             <if test="userId != null">#{userId},</if>
             <if test="deptId != null">#{deptId},</if>
             <if test="defaultLoading != null">#{defaultLoading},</if>
@@ -83,10 +83,10 @@
             <if test="tilesName != null">tiles_name = #{tilesName},</if>
             <if test="tilesHeight != null">tiles_height = #{tilesHeight},</if>
             <if test="tileAddress != null">tile_address = #{tileAddress},</if>
-            <if test="screenSpaceError != null">screen_space_error = #{screenSpaceError},</if>
+            <if test="maximumScreenSpaceError != null">maximum_screen_space_error = #{maximumScreenSpaceError},</if>
             <if test="maximumMemoryUsage != null">maximum_memory_usage = #{maximumMemoryUsage},</if>
-            <if test="automaticSuperCleaningModel != null">automatic_super_cleaning_model = #{automaticSuperCleaningModel},</if>
-            <if test="loadingSpeed != null">loading_speed = #{loadingSpeed},</if>
+            <if test="cullRequestsWhileMovingMultiplier != null">cull_requests_while_moving_multiplier = #{cullRequestsWhileMovingMultiplier},</if>
+            <if test="dynamicScreenSpaceErrorDensity != null">dynamic_screen_space_error_density = #{dynamicScreenSpaceErrorDensity},</if>
             <if test="userId != null">user_id = #{userId},</if>
             <if test="deptId != null">dept_id = #{deptId},</if>
             <if test="defaultLoading != null">default_loading = #{defaultLoading},</if>
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..6e89232
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/component/websocket/handler/NoFlyDistanceManager.java
@@ -0,0 +1,100 @@
+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;
+
+            // ⭐ 外扩 50 米
+            ArdNoFlyBox expanded = expandBoxByMeter(box, 50);
+
+            // ⭐ 包围盒快速排除必须使用 expanded,而不是 box
+            if (current.x < expanded.getMinx() || current.x > expanded.getMaxx() ||
+                    current.y < expanded.getMiny() || current.y > expanded.getMaxy()) {
+                continue;
+            }
+
+            // 计算 polygon 距离
+            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();
+    }
+
+    // 外扩包围盒多少米
+    private ArdNoFlyBox expandBoxByMeter(ArdNoFlyBox box, double meter) {
+
+        // 计算经纬度偏移
+        double latShift = meter / 111320.0;  // 纬度方向
+        double lngShift = meter / (111320.0 * Math.cos(Math.toRadians(
+                (box.getMiny() + box.getMaxy()) / 2
+        ))); // 经度方向与纬度相关
+
+        return new ArdNoFlyBox(
+                box.getMinx() - lngShift,
+                box.getMiny() - latShift,
+                box.getMaxx() + lngShift,
+                box.getMaxy() + latShift
+        );
+    }
+
+
+    @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