liusuyi
2025-11-13 3756cf0e3841910b22998bc0edf8d209e8539556
增加禁飞区工具类
5 files modified
1 files deleted
459 ■■■■■ changed files
ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInforServiceImpl.java 8 ●●●●● patch | view | raw | blame | history
ard-system/pom.xml 7 ●●●●● patch | view | raw | blame | history
ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyZone.java 10 ●●●●● patch | view | raw | blame | history
ard-system/src/main/java/com/ard/data/noflyzone/utils/GeoUtils.java 171 ●●●●● patch | view | raw | blame | history
ard-system/src/main/java/com/ard/data/noflyzone/utils/NoFlyZoneUtils.java 197 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/aspect/UavControlAspect.java 66 ●●●●● patch | view | raw | blame | history
ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInforServiceImpl.java
@@ -217,10 +217,10 @@
        List<ArdNoFlyZone> ardNoFlyZones = ardNoFlyZoneService.selectArdNoFlyZoneList(new ArdNoFlyZone());
        if (!ardNoFlyZones.isEmpty()) {
            Vertex vertex = new Vertex(longitude, latitude);
            boolean inNoFlyZones = NoFlyZoneUtils.isInNoFlyZones(vertex, ardNoFlyZones);
            if (inNoFlyZones) {
                throw new ControlException("目标点在禁飞区内,无法执行操作!");
            }
//            boolean inNoFlyZones = NoFlyZoneUtils.isPointInPolygon(vertex, ardNoFlyZones);
//            if (inNoFlyZones) {
//                throw new ControlException("目标点在禁飞区内,无法执行操作!");
//            }
        }
        return true;
    }
ard-system/pom.xml
@@ -22,6 +22,13 @@
            <groupId>com.ard</groupId>
            <artifactId>ard-common</artifactId>
        </dependency>
        <dependency>
            <groupId>org.locationtech.jts</groupId>
            <artifactId>jts-core</artifactId>
            <version>1.19.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyZone.java
@@ -1,11 +1,11 @@
package com.ard.data.noflyzone.domain;
import com.ard.data.noflyzone.utils.GeoUtils;
import com.ard.common.annotation.Excel;
import com.ard.common.core.domain.BaseEntity;
import com.ard.data.noflyzone.utils.NoFlyZoneUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.ard.common.annotation.Excel;
import com.ard.common.core.domain.BaseEntity;
import java.util.List;
@@ -41,14 +41,14 @@
    // 懒加载 / 动态计算
    public ArdNoFlyBox getArdNoFlyBox() {
        if (ardNoFlyBox == null && geojson != null) {
            ardNoFlyBox = GeoUtils.getArdNoFlyBox(geojson);
            ardNoFlyBox = NoFlyZoneUtils.getArdNoFlyBox(geojson);
        }
        return ardNoFlyBox;
    }
    public List<Vertex> getVertices() {
        if (vertices == null && geojson != null) {
            vertices = GeoUtils.toVertexes(geojson);
            vertices = NoFlyZoneUtils.toVertexes(geojson);
        }
        return vertices;
    }
ard-system/src/main/java/com/ard/data/noflyzone/utils/GeoUtils.java
File was deleted
ard-system/src/main/java/com/ard/data/noflyzone/utils/NoFlyZoneUtils.java
@@ -2,104 +2,169 @@
import com.ard.data.noflyzone.domain.ArdNoFlyBox;
import com.ard.data.noflyzone.domain.ArdNoFlyZone;
import com.ard.data.noflyzone.domain.FeatureCollection;
import com.ard.data.noflyzone.domain.Vertex;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Polygon;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class NoFlyZoneUtils {
    /**
     * 判断点是否在禁飞区集合中
     *
     * @param point   目标点
     * @param zones   禁飞区集合
     * @return true: 点在任意禁飞区内
     * 判断点是否在多边形内(射线法)
     */
    public static boolean isInNoFlyZones(Vertex point, List<ArdNoFlyZone> zones) {
        if (point == null || zones == null || zones.isEmpty()) {
            return false;
    public static boolean isPointInPolygon(Vertex point, List<Vertex> polygon) {
        int n = polygon.size();
        boolean inside = false;
        for (int i = 0, j = n - 1; i < n; j = i++) {
            Vertex vi = polygon.get(i);
            Vertex vj = polygon.get(j);
            boolean intersect = ((vi.y > point.y) != (vj.y > point.y)) &&
                    (point.x < (vj.x - vi.x) * (point.y - vi.y) / (vj.y - vi.y) + vi.x);
            if (intersect) inside = !inside;
        }
        return inside;
    }
        for (ArdNoFlyZone zone : zones) {
            // 1️⃣ 先快速判断外接矩形
            if (zone.getArdNoFlyBox() != null &&
                !isInBox(point, zone.getArdNoFlyBox())) {
                continue; // 点不在外接矩形内,跳过
            }
            // 2️⃣ 多边形判断
            List<Vertex> vertices = zone.getVertices();
            if (vertices != null && !vertices.isEmpty()) {
                if (isPointInPolygon(point, vertices)) {
                    return true;
                }
    /**
     * 判断线段是否与多边形相交
     */
    public static boolean isLineIntersectPolygon(Vertex p1, Vertex p2, List<Vertex> polygon) {
        int n = polygon.size();
        for (int i = 0; i < n; i++) {
            Vertex a = polygon.get(i);
            Vertex b = polygon.get((i + 1) % n);
            if (isSegmentsIntersect(p1, p2, a, b)) {
                return true;
            }
        }
        return false;
    }
    /**
     * 判断点是否在矩形框内
     * 判断两条线段是否相交
     */
    private static boolean isInBox(Vertex point, ArdNoFlyBox box) {
        return point.x >= box.getMinx() && point.x <= box.getMaxx() &&
               point.y >= box.getMiny() && point.y <= box.getMaxy();
    }
    private static boolean isSegmentsIntersect(Vertex p1, Vertex p2, Vertex p3, Vertex p4) {
        double d1 = cross(p3, p4, p1);
        double d2 = cross(p3, p4, p2);
        double d3 = cross(p1, p2, p3);
        double d4 = cross(p1, p2, p4);
    /**
     * 射线法判断点是否在多边形内
     */
    private static boolean isPointInPolygon(Vertex point, List<Vertex> vertices) {
        if (point == null || vertices == null || vertices.size() < 3) {
            return false;
        if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) &&
                ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) {
            return true;
        }
        int count = 0;
        int n = vertices.size();
        double epsilon = 1e-9;
        // 处理共线情况
        if (d1 == 0 && onSegment(p3, p4, p1)) return true;
        if (d2 == 0 && onSegment(p3, p4, p2)) return true;
        if (d3 == 0 && onSegment(p1, p2, p3)) return true;
        if (d4 == 0 && onSegment(p1, p2, p4)) return true;
        for (int i = 0; i < n; i++) {
            Vertex v1 = vertices.get(i);
            Vertex v2 = vertices.get((i + 1) % n);
        return false;
    }
            // 点就是顶点
            if (v1.IsSame(point)) {
                return true;
            }
    private static double cross(Vertex p1, Vertex p2, Vertex p3) {
        return (p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y);
    }
            double minX = Math.min(v1.x, v2.x);
            double maxX = Math.max(v1.x, v2.x);
            double minY = Math.min(v1.y, v2.y);
            double maxY = Math.max(v1.y, v2.y);
    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);
    }
            // 水平线段
            if (Math.abs(minY - maxY) < epsilon) {
                if (Math.abs(point.y - minY) < epsilon && point.x >= minX && point.x <= maxX) {
                    return true;
                } else {
                    continue;
                }
            }
            // 点不在纵向范围
            if (point.y < minY || point.y > maxY) {
    /**
     * 根据 GeoJSON(FeatureCollection)计算外接矩形
     */
    @SuppressWarnings("unchecked")
    public static ArdNoFlyBox getArdNoFlyBox(FeatureCollection featureCollection) {
        if (featureCollection == null || featureCollection.getFeatures() == null) {
            return null;
        }
        double minx = Double.MAX_VALUE;
        double miny = Double.MAX_VALUE;
        double maxx = Double.MIN_VALUE;
        double maxy = Double.MIN_VALUE;
        for (FeatureCollection.Feature feature : featureCollection.getFeatures()) {
            FeatureCollection.Geometry geometry = feature.getGeometry();
            if (geometry == null || geometry.getCoordinates() == null) {
                continue;
            }
            double xIntersect = v1.x + (point.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y);
            String type = geometry.getType();
            Object coords = geometry.getCoordinates();
            if (Math.abs(xIntersect - point.x) < epsilon) {
                return true; // 点在边界
            if ("Polygon".equalsIgnoreCase(type)) {
                // Polygon: [[[lon, lat], [lon, lat], ...]]
                List<List<List<Double>>> rings = (List<List<List<Double>>>) coords;
                for (List<List<Double>> ring : rings) {
                    for (List<Double> point : ring) {
                        if (point.size() < 2) {
                            continue;
                        }
                        double x = point.get(0);
                        double y = point.get(1);
                        minx = Math.min(minx, x);
                        miny = Math.min(miny, y);
                        maxx = Math.max(maxx, x);
                        maxy = Math.max(maxy, y);
                    }
                }
            }
            if (xIntersect > point.x) {
                count++;
            }
            if (Math.abs(point.y - minY) < epsilon) {
                count--;
            else if ("MultiPolygon".equalsIgnoreCase(type)) {
                // MultiPolygon: [[[[lon, lat], ...]], ...]
                List<List<List<List<Double>>>> polygons = (List<List<List<List<Double>>>>) coords;
                for (List<List<List<Double>>> polygon : polygons) {
                    for (List<List<Double>> ring : polygon) {
                        for (List<Double> point : ring) {
                            if (point.size() < 2) {
                                continue;
                            }
                            double x = point.get(0);
                            double y = point.get(1);
                            minx = Math.min(minx, x);
                            miny = Math.min(miny, y);
                            maxx = Math.max(maxx, x);
                            maxy = Math.max(maxy, y);
                        }
                    }
                }
            }
        }
        return count % 2 != 0;
        return new ArdNoFlyBox(minx, miny, maxx, maxy);
    }
    /**
     * 将 GeoJSON Polygon 的外环坐标转换成 Vertex 列表
     */
    public static List<Vertex> toVertexes(FeatureCollection featureCollection) {
        List<Vertex> vertexes = new ArrayList<>();
        if (featureCollection == null || featureCollection.getFeatures() == null || featureCollection.getFeatures().isEmpty()) {
            return vertexes;
        }
        FeatureCollection.Feature feature = featureCollection.getFeatures().get(0);
        if (feature.getGeometry() != null) {
            List<List<List<Double>>> coords = feature.getGeometry().getCoordinates();
            if (coords != null && !coords.isEmpty()) {
                List<List<Double>> outerRing = coords.get(0);
                for (List<Double> point : outerRing) {
                    if (point.size() >= 2) {
                        vertexes.add(new Vertex(point.get(0), point.get(1)));
                    }
                }
            }
        }
        return vertexes;
    }
}
ard-work/src/main/java/com/ard/work/aspect/UavControlAspect.java
@@ -23,7 +23,6 @@
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
@@ -144,34 +143,61 @@
            return;
        }
        Double longitude = null;
        Double latitude = null;
        // ① 提取 boxSn(无人机编号)和目标点坐标
        String boxSn = null;
        Double targetLon = null, targetLat = null;
        for (Object arg : args) {
            if (arg == null) {
                continue;
            if (arg == null) continue;
            if (boxSn == null) {
                String sn = ReflectUtils.getFieldValue(arg, "boxSn");
                if (sn != null) boxSn = sn;
            }
            Double lon = ReflectUtils.getFieldValue(arg, "longitude");
            Double lat = ReflectUtils.getFieldValue(arg, "latitude");
            if (lon != null && lat != null) {
                longitude = lon;
                latitude = lat;
                break;
            if (targetLon == null || targetLat == null) {
                Double lon = ReflectUtils.getFieldValue(arg, "longitude");
                Double lat = ReflectUtils.getFieldValue(arg, "latitude");
                if (lon != null && lat != null) {
                    targetLon = lon;
                    targetLat = lat;
                }
            }
        }
        if (longitude == null || latitude == null) {
            throw new ControlException("方法参数中未找到有效经纬度字段,无法判断禁飞区!");
        if (targetLon == null || targetLat == null) {
            throw new ControlException("方法参数中未找到有效目标经纬度字段,无法判断禁飞区!");
        }
        List<ArdNoFlyZone> ardNoFlyZones = ardNoFlyZoneService.selectArdNoFlyZoneList(new ArdNoFlyZone());
        if (!ardNoFlyZones.isEmpty()) {
            Vertex vertex = new Vertex(longitude, latitude);
            boolean inNoFlyZones = NoFlyZoneUtils.isInNoFlyZones(vertex, ardNoFlyZones);
            if (inNoFlyZones) {
                throw new ControlException("目标点在禁飞区内,无法执行操作!");
        // ② 获取无人机当前位置(从缓存)
        Vertex current = null;
        if (boxSn != null) {
            TelemetryData telemetry = UavTelemetryDataCache.get(boxSn);
            if (telemetry != null) {
                current = new Vertex(telemetry.getLng(), telemetry.getLat());
                //current = new Vertex(125.03, 46.58);
            }
        }
        Vertex target = new Vertex(targetLon, targetLat);
        // ③ 查询禁飞区
        List<ArdNoFlyZone> zones = ardNoFlyZoneService.selectArdNoFlyZoneList(new ArdNoFlyZone());
        if (zones.isEmpty()) return;
        for (ArdNoFlyZone zone : zones) {
            List<Vertex> polygon = zone.getVertices();
            if (polygon == null || polygon.size() < 3) continue;
            // 检查目标点是否在禁飞区
            if (NoFlyZoneUtils.isPointInPolygon(target, polygon)) {
                throw new ControlException("目标点在禁飞区【" + zone.getName() + "】内,无法执行操作!");
            }
            // 检查路径是否穿越禁飞区
            if (current != null && NoFlyZoneUtils.isLineIntersectPolygon(current, target, polygon)) {
                throw new ControlException("路径穿越禁飞区【" + zone.getName() + "】,无法执行操作!");
            }
        }
    }
}