liusuyi
2025-12-12 7a164e24123df74c3228324e4a398b79cb9c12b6
增加禁飞区距离计算
3 files added
3 files modified
239 ■■■■■ changed files
ard-system/src/main/java/com/ard/data/noflyzone/utils/NoFlyZoneUtils.java 78 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/component/websocket/handler/NoFlyDistanceManager.java 76 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java 36 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/domain/dto/NoFlyDistanceDTO.java 18 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/domain/enums/MessageType.java 1 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/domain/message/NoFlyDistanceMessage.java 30 ●●●●● patch | view | raw | blame | history
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")
ard-work/src/main/java/com/ard/work/component/websocket/handler/NoFlyDistanceManager.java
New file
@@ -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";
    }
}
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");
ard-work/src/main/java/com/ard/work/domain/dto/NoFlyDistanceDTO.java
New file
@@ -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
}
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;
ard-work/src/main/java/com/ard/work/domain/message/NoFlyDistanceMessage.java
New file
@@ -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;
}