From 36503d07775d54085464405cb8f35a30b29d8fe7 Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: Fri, 17 Oct 2025 13:43:24 +0800
Subject: [PATCH] 优化禁飞区增加缓冲区

---
 ard-system/src/main/java/com/ard/data/noflyzone/domain/Vertex.java       |   30 +++++++
 ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyBox.java  |   15 +++
 ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyZone.java |   24 ++++++
 ard-system/src/main/java/com/ard/data/noflyzone/utils/GeoUtils.java      |  171 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 240 insertions(+), 0 deletions(-)

diff --git a/ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyBox.java b/ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyBox.java
new file mode 100644
index 0000000..9d5ecf1
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyBox.java
@@ -0,0 +1,15 @@
+package com.ard.data.noflyzone.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ArdNoFlyBox {
+    private double minx;
+    private double miny;
+    private double maxx;
+    private double maxy;
+}
diff --git a/ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyZone.java b/ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyZone.java
index ea87870..477377e 100644
--- a/ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyZone.java
+++ b/ard-system/src/main/java/com/ard/data/noflyzone/domain/ArdNoFlyZone.java
@@ -1,10 +1,13 @@
 package com.ard.data.noflyzone.domain;
 
+import com.ard.data.noflyzone.utils.GeoUtils;
 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;
 
 /**
  * 禁飞区管理对象 ard_no_fly_zone
@@ -29,4 +32,25 @@
     /** 数据json */
     @Excel(name = "数据json")
     private FeatureCollection geojson;
+
+    /** 禁飞区外接矩形 */
+    private ArdNoFlyBox ardNoFlyBox;
+
+    private List<Vertex> vertices;
+
+    // 懒加载 / 动态计算
+    public ArdNoFlyBox getArdNoFlyBox() {
+        if (ardNoFlyBox == null && geojson != null) {
+            ardNoFlyBox = GeoUtils.getArdNoFlyBox(geojson);
+        }
+        return ardNoFlyBox;
+    }
+
+    public List<Vertex> getVertices() {
+        if (vertices == null && geojson != null) {
+            vertices = GeoUtils.toVertexes(geojson);
+        }
+        return vertices;
+    }
+
 }
diff --git a/ard-system/src/main/java/com/ard/data/noflyzone/domain/Vertex.java b/ard-system/src/main/java/com/ard/data/noflyzone/domain/Vertex.java
new file mode 100644
index 0000000..eae5166
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/noflyzone/domain/Vertex.java
@@ -0,0 +1,30 @@
+package com.ard.data.noflyzone.domain;
+
+public class Vertex {
+	public double x;
+	public double y;
+
+	public Vertex() {
+
+	}
+
+	public Vertex(double x, double y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	// 判断这个点是不是和另外一个点重合(允许极小的误差,算作同一个点
+	public boolean IsSame(Vertex anotherV) {
+		double almostZero = 0.00000001;
+		if ((Math.abs(x - anotherV.x) < almostZero) && (Math.abs(y - anotherV.y) < almostZero)) {
+            return true;
+        } else {
+            return false;
+        }
+	}
+
+	// 直线距离
+	public double Distance(Vertex anotherV) {
+		return Math.sqrt(Math.pow(x - anotherV.x, 2) + Math.pow(y - anotherV.y, 2));
+	}
+}
\ No newline at end of file
diff --git a/ard-system/src/main/java/com/ard/data/noflyzone/utils/GeoUtils.java b/ard-system/src/main/java/com/ard/data/noflyzone/utils/GeoUtils.java
new file mode 100644
index 0000000..0c8e045
--- /dev/null
+++ b/ard-system/src/main/java/com/ard/data/noflyzone/utils/GeoUtils.java
@@ -0,0 +1,171 @@
+package com.ard.data.noflyzone.utils;
+
+import com.ard.data.noflyzone.domain.ArdNoFlyBox;
+import com.ard.data.noflyzone.domain.FeatureCollection;
+import com.ard.data.noflyzone.domain.Vertex;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 几何工具类
+ */
+public class GeoUtils {
+    /**
+     * 根据 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;
+            }
+
+            String type = geometry.getType();
+            Object coords = geometry.getCoordinates();
+
+            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);
+                    }
+                }
+            }
+            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 new ArdNoFlyBox(minx, miny, maxx, maxy);
+    }
+    /**
+     * 判断点是否在多边形内(包括边界)
+     * 射线法:从该点向右发射水平射线,计算交点个数
+     *
+     * @param vertex 点对象
+     * @param vertexes 多边形顶点列表(按顺序闭合)
+     * @return true 表示点在多边形内或在边上
+     */
+    public static boolean isVertexInPoly(Vertex vertex, List<Vertex> vertexes) {
+        if (vertex == null || vertexes == null || vertexes.size() < 3) {
+            return false; // 无效输入
+        }
+
+        int count = 0;
+        int n = vertexes.size();
+
+        for (int i = 0; i < n; i++) {
+            Vertex v1 = vertexes.get(i);
+            Vertex v2 = vertexes.get((i + 1) % n);
+
+            // 1. 点就是多边形的顶点
+            if (v1.IsSame(vertex)) {
+                return true;
+            }
+
+            // 2. 点在线段上
+            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);
+
+            // 水平线段
+            if (minY == maxY) {
+                if (vertex.y == minY && vertex.x >= minX && vertex.x <= maxX) {
+                    return true;
+                } else {
+                    continue; // 不计入交点
+                }
+            }
+
+            // 点在线段纵向范围外
+            if (vertex.y < minY || vertex.y > maxY) {
+                continue;
+            }
+
+            // 计算射线交点的 X 坐标
+            double xIntersect = v1.x + (vertex.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y);
+
+            // 交点在点左侧,不计入
+            if (xIntersect < vertex.x) {
+                continue;
+            }
+
+            // 交点就在点上,认为在边界
+            if (xIntersect == vertex.x) {
+                return true;
+            }
+
+            // 避免射线穿过线段下端点重复计数
+            if (vertex.y == minY) {
+                continue;
+            }
+
+            // 其它情况,交点计数
+            count++;
+        }
+
+        // 奇数交点 → 点在多边形内
+        return count % 2 != 0;
+    }
+
+    /**
+     * 将 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;
+    }
+
+}

--
Gitblit v1.9.3