From 6cf5d9645686477280ebb30a11ab916eb1f33b73 Mon Sep 17 00:00:00 2001
From: aix <vip_xiaobin810@163.com>
Date: Fri, 19 Jul 2024 16:23:42 +0800
Subject: [PATCH] 新建面状航线

---
 src/main/java/com/dji/sample/wayline/plane/PlaneCourseUtils.java                        |  342 ++++++++++++++++++++++++++++++++++++++
 src/main/java/com/dji/sample/wayline/plane/param/CreateWaylineParam.java                |   31 +++
 src/main/java/com/dji/sample/wayline/plane/BoundingRectangle.java                       |   69 +++++++
 src/main/java/com/dji/sample/wayline/plane/MapLatLng.java                               |   28 +++
 src/main/java/com/dji/sample/wayline/plane/controller/PlaneCreateWayLineController.java |   30 +++
 5 files changed, 500 insertions(+), 0 deletions(-)

diff --git a/src/main/java/com/dji/sample/wayline/plane/BoundingRectangle.java b/src/main/java/com/dji/sample/wayline/plane/BoundingRectangle.java
new file mode 100644
index 0000000..60fccf9
--- /dev/null
+++ b/src/main/java/com/dji/sample/wayline/plane/BoundingRectangle.java
@@ -0,0 +1,69 @@
+package com.dji.sample.wayline.plane;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @Author AIX
+ * @Date 2024/7/11 11:12
+ * @Version 1.0
+ */
+@Data
+public class BoundingRectangle {
+
+    public MapLatLng topLeft;
+    public MapLatLng topRight;
+    public MapLatLng bottomRight;
+    public MapLatLng bottomLeft;
+
+    // 构造器
+    // 构造函数,使用四个角点初始化外接矩形
+    public BoundingRectangle(MapLatLng topLeft, MapLatLng topRight, MapLatLng bottomRight, MapLatLng bottomLeft) {
+        this.topLeft = topLeft;
+        this.topRight = topRight;
+        this.bottomRight = bottomRight;
+        this.bottomLeft = bottomLeft;
+    }
+
+    /**
+     * 静态方法,根据多边形点列表计算外接矩形并返回BoundingRectangle实例
+     * @param polygon
+     * @return
+     */
+    public static BoundingRectangle fromPolygon(List<double[]> polygon) {
+        double maxLat = Double.NEGATIVE_INFINITY;
+        double minLat = Double.POSITIVE_INFINITY;
+        double maxLon = Double.NEGATIVE_INFINITY;
+        double minLon = Double.POSITIVE_INFINITY;
+
+        // 遍历多边形点以找到边界
+        for (double[] point : polygon) {
+            double lat = point[1]; // 假设每个点的数组是先经度[0]后纬度[1]
+            double lon = point[0];
+
+            if (lat > maxLat) {
+                maxLat = lat;
+            }
+            if (lat < minLat) {
+                minLat = lat;
+            }
+            if (lon > maxLon) {
+                maxLon = lon;
+            }
+            if (lon < minLon) {
+                minLon = lon;
+            }
+        }
+
+        // 创建外接矩形的四个角点
+        MapLatLng topLeft = new MapLatLng(maxLat, minLon);
+        MapLatLng topRight = new MapLatLng(maxLat, maxLon);
+        MapLatLng bottomRight = new MapLatLng(minLat, maxLon);
+        MapLatLng bottomLeft = new MapLatLng(minLat, minLon);
+
+        // 返回BoundingRectangle实例
+        return new BoundingRectangle(topLeft, topRight, bottomRight, bottomLeft);
+    }
+
+}
diff --git a/src/main/java/com/dji/sample/wayline/plane/MapLatLng.java b/src/main/java/com/dji/sample/wayline/plane/MapLatLng.java
new file mode 100644
index 0000000..bca3f6f
--- /dev/null
+++ b/src/main/java/com/dji/sample/wayline/plane/MapLatLng.java
@@ -0,0 +1,28 @@
+package com.dji.sample.wayline.plane;
+
+import lombok.Data;
+
+/**
+ * @Author AIX
+ * @Date 2024/7/9 21:40
+ * @Version 1.0
+ */
+@Data
+public class MapLatLng {
+
+    private double latitude;
+    private double longitude;
+
+    public MapLatLng(double lat, double lng) {
+        this.latitude = lat;
+        this.longitude = lng;
+    }
+
+    @Override
+    public String toString() {
+        return "{" +
+                "longitude:" +  this.longitude +
+                ",\nlatitude:" +  this.latitude +
+                '}';
+    }
+}
diff --git a/src/main/java/com/dji/sample/wayline/plane/PlaneCourseUtils.java b/src/main/java/com/dji/sample/wayline/plane/PlaneCourseUtils.java
new file mode 100644
index 0000000..bc780cb
--- /dev/null
+++ b/src/main/java/com/dji/sample/wayline/plane/PlaneCourseUtils.java
@@ -0,0 +1,342 @@
+package com.dji.sample.wayline.plane;
+
+import com.dji.sample.patches.utils.GeoToolsUtil;
+import com.dji.sample.wayline.plane.param.CreateWaylineParam;
+import org.geotools.geometry.jts.JTSFactoryFinder;
+import org.locationtech.jts.geom.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author AIX
+ * @Date 2024/7/9 21:33
+ * 面状航线
+ * @Version 1.0
+ */
+public class PlaneCourseUtils {
+
+    /**
+     * @param height 无人机高度,单位米(m)
+     * @param frame  画幅
+     * @param focal  焦距
+     * @param ratio  重叠率
+     */
+    private static double get(double height, double frame, double focal, double ratio) {
+        //todo 如果focal焦距为0的话,则使用默认值值24毫米
+        focal = focal == 0 ? 24 : focal;
+        //单位换成米
+        focal = focal / 1000;
+        frame = frame / 1000;
+        //设呈现的真实距离为x
+        double x;
+        //拍摄到的距离
+        x = frame * height / focal;
+        //设重叠距离
+        double d;
+        //重叠部分的距离
+        d = ratio * x;
+        //非重叠部分的距离 (单位米)
+        d = x - d;
+        return d;
+    }
+
+    /**
+     * 方位角算法:已知一点经纬度,方位角,距离,求另一点经纬度。
+     *
+     * @param latLng   经纬度
+     * @param distance 距离
+     * @param bearing  方位角
+     * @return
+     */
+    private static MapLatLng getBearingLatLng(MapLatLng latLng, int distance, double bearing) {
+
+        double R = 6371.393 * 1000;
+
+        double δ = distance / R;
+        double θ = toRadius(bearing);
+
+        double φ1 = toRadius(latLng.getLatitude());
+        double λ1 = toRadius(latLng.getLongitude());
+
+        double sinφ2 = Math.sin(φ1) * Math.cos(δ) + Math.cos(φ1) * Math.sin(δ) * Math.cos(θ);
+        double φ2 = Math.asin(sinφ2);
+
+        double y = Math.sin(θ) * Math.sin(δ) * Math.cos(φ1);
+        double x = Math.cos(δ) - Math.sin(φ1) * sinφ2;
+        double λ2 = λ1 + Math.atan2(y, x);
+
+        double lat = toDegree(φ2);
+        double lng = toDegree(λ2);
+
+        return new MapLatLng(lat, lng);
+    }
+
+    private static double toRadius(double value) {
+        return value * Math.PI / 180;
+    }
+
+    private static double toDegree(double value) {
+        return value * 180 / Math.PI;
+    }
+
+    /**
+     * 最北纬点的经纬度
+     *
+     * @param polygon 面集合
+     * @return
+     */
+    private static MapLatLng maxLatPoint(List<double[]> polygon) {
+        // 查找最北的点
+        double maxLat = Double.MIN_VALUE;
+        double[] maxLatPoint = null;
+
+        for (double[] point : polygon) {
+            if (point[1] > maxLat) {
+                maxLat = point[1];
+                maxLatPoint = point;
+            }
+        }
+        return new MapLatLng(maxLatPoint[1], maxLatPoint[0]);
+    }
+
+    /**
+     * 最南纬点的经纬度
+     *
+     * @param polygon 面集合
+     * @return
+     */
+    private static MapLatLng minLatPoint(List<double[]> polygon) {
+        // 查最南的点
+        double minLat = Double.MAX_VALUE;
+        double[] minLatPoint = null;
+
+        for (double[] point : polygon) {
+            if (point[1] < minLat) {
+                minLat = point[1];
+                minLatPoint = point;
+            }
+        }
+        return new MapLatLng(minLatPoint[1], minLatPoint[0]);
+    }
+
+    /**
+     * 根据左上角经纬度生成面点集合
+     *
+     * @param mapLatLng    从哪个点开始
+     * @param intValue     距离
+     * @param bearing      角度
+     * @param mapLatLngMin 面最南经纬度
+     */
+    private static List<MapLatLng> createPoint(MapLatLng mapLatLng, int intValue, double bearing, MapLatLng mapLatLngMin) {
+
+        List<MapLatLng> pointList = new ArrayList<>();
+        while (true) {
+            MapLatLng newLocation = getBearingLatLng(mapLatLng, intValue, bearing);
+            if (newLocation.getLatitude() <= mapLatLngMin.getLatitude())
+                break;
+
+            mapLatLng = newLocation;
+            pointList.add(newLocation);
+            System.out.println(newLocation + ",");
+        }
+        return pointList;
+    }
+
+    public static List<Coordinate> createWaylinePoints(CreateWaylineParam param) {
+        // 获取外接矩形
+        BoundingRectangle boundingRectangle = BoundingRectangle.fromPolygon(param.getPolygon());
+
+        // 航向重叠计算  待完成
+
+        // 旁向重叠计算
+        double distance2 = PlaneCourseUtils.get(param.getHeight(), param.getFrame(), param.getFocal(), 0.8);
+        System.out.println("旁向重叠计算距离:" + distance2 * 100 / 100);
+        int intValue = (int) distance2;
+
+        // 以最北纬度为基准点,在航线间隔距离下,
+        // 计算出 180 度方向(正北为0度)的一个点,该点则是第一条航线上的点,
+        // 然后再通过该点为基准点,一直反复计算出下一个点,直到下一个航点的位置小于最南纬度为止
+
+        //最南纬点经纬度
+        MapLatLng mapLatLngMin = PlaneCourseUtils.minLatPoint(param.getPolygon());
+
+        List<MapLatLng> topLeftPointList = PlaneCourseUtils.createPoint(boundingRectangle.topLeft, intValue, param.getBearing(), mapLatLngMin);
+        List<MapLatLng> topRightPointList = PlaneCourseUtils.createPoint(boundingRectangle.topRight, intValue, param.getBearing(), mapLatLngMin);
+
+        List<LineString> lineStrings = PlaneCourseUtils.createLine(topLeftPointList, topRightPointList);
+
+        List<Coordinate> points = PlaneCourseUtils.getPoints(param.getPolygon(), lineStrings);
+
+        return points;
+    }
+
+    //MULTIPOLYGON(((115.86528871951153 28.625287925196325,
+    // 115.86561708513025 28.625787612007546,
+    // 115.86815834948467 28.624602640426623,
+    // 115.86773004650362 28.62425999804172,
+    // 115.86528871951153 28.625287925196325)))
+    public static void main(String[] args) {
+
+        //外接矩形最北经纬度和最南经纬度
+        List<double[]> polygon = new ArrayList<>();
+        double[] a = {116.028037250229,25.8948290570725};
+        double[] a2 = {116.031565260299,25.8950672687002};
+        double[] a3 = {116.032112214864,25.8901339309971};
+        double[] a4 = {116.028726270654,25.8895106128711};
+        polygon.add(a);
+        polygon.add(a2);
+        polygon.add(a3);
+        polygon.add(a4);
+        BoundingRectangle boundingRectangle = BoundingRectangle.fromPolygon(polygon);
+        System.out.println("northLatLng:" + boundingRectangle);
+
+        //最北纬度点
+        MapLatLng mapLatLngMax = maxLatPoint(polygon);
+        MapLatLng mapLatLngMin = minLatPoint(polygon);
+        System.out.println("最北纬度点:" + mapLatLngMax);
+        System.out.println("最南纬度点:" + mapLatLngMin);
+
+        // 航向重叠计算
+        //已知短画幅=24mm,假设从无人机拿到的焦距是26mm,高度设置的是100米,重叠率设置80%,则无人机真正拍摄到的距离差为:
+        // m30t:5.0125   焦距:21-75 mm(等效焦距:113-405 mm)
+        // m3d:6.9999943   等效焦距:24 mm
+        double distance1 = get(100, 24, 26, 0.8);
+        System.out.println("航向重叠计算距离" + distance1);
+
+        // 旁向重叠计算
+        //旁向重叠的计算影响航线的的间隔,对于一块固定地表,间隔越宽,则飞行的航线就越少,
+        // 间隔越窄,则飞行的航线就越多,主要影响间隔的就是重叠率。
+        // 已知长画幅=35mm,假设从无人机拿到的焦距是26mm,高度设置的是100米,重叠率设置80%,则航线间距离为:
+        // 为什么是26mm   对焦距离 = 5 + 最小焦距 = 21
+        double distance2 = get(372.3, 100, 10*6.9999943, 0.8);
+        System.out.println("旁向重叠计算距离:" + distance2 * 100 / 100);
+        int intValue = (int) distance2;
+
+        // 创建一个起始的MapLatLng对象 以最北纬度为基准点
+//        MapLatLng startLocation = new MapLatLng(boundingRectangle.bottomLeft.getLatitude(), boundingRectangle.bottomLeft.getLongitude()); // 经纬度
+//
+        // 定义要移动的距离(单位:米)和方位角(单位:度)
+        double bearing = 180; // 方向
+
+        // 调用getBearingLatLng方法计算新位置
+        System.out.println("第一条线路:" + mapLatLngMax);
+        System.out.println("------------------------------------------------");
+
+        // 以最北纬度为基准点,在航线间隔距离下,
+        // 计算出 180 度方向(正北为0度)的一个点,该点则是第一条航线上的点,
+        // 然后再通过该点为基准点,一直反复计算出下一个点,直到下一个航点的位置小于最南纬度为止
+        List<MapLatLng> topLeftPointList = createPoint(boundingRectangle.topLeft, intValue, bearing, mapLatLngMin);
+        List<MapLatLng> topRightPointList = createPoint(boundingRectangle.topRight, intValue, bearing, mapLatLngMin);
+
+        List<LineString> lineStrings = createLine(topLeftPointList, topRightPointList);
+
+        List<Coordinate> points = getPoints(polygon, lineStrings);
+        for (Coordinate point:points) {
+            System.out.println("" + point.x + ", " + point.y + ";");
+        }
+
+    }
+
+    /**
+     * 创建线的集合
+     *
+     * @param topLeftPointList  左上角点集合
+     * @param topRightPointList 右上角点集合
+     */
+    private static List<LineString> createLine(List<MapLatLng> topLeftPointList, List<MapLatLng> topRightPointList) {
+        // 创建GeometryFactory
+        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
+
+        //线的集合
+        List<LineString> lineStrings = new ArrayList<>();
+        // 定义两点来创建LineString
+        for (int i = 0; i < topLeftPointList.size(); i++) {
+            MapLatLng topLeft = topLeftPointList.get(i);
+            MapLatLng topRight = topRightPointList.get(i);
+            Coordinate start = new Coordinate(topLeft.getLongitude(), topRight.getLatitude(), 0);
+            Coordinate end = new Coordinate(topRight.getLongitude(), topRight.getLatitude(), 0);
+            LineString line = geometryFactory.createLineString(new Coordinate[]{start, end});
+            lineStrings.add(line);
+            System.out.println("line:" + line);
+        }
+        return lineStrings;
+    }
+
+    /**
+     * 获取面和线的交集点集合
+     *
+     * @param polygonNums
+     * @param lineStrings
+     */
+    private static List<Coordinate> getPoints(List<double[]> polygonNums, List<LineString> lineStrings) {
+
+        // 创建GeometryFactory
+        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
+
+        // 检查polygonNums是否为空或长度小于3(至少需要三个点才能形成一个闭合多边形)
+        if (polygonNums == null || polygonNums.size() < 3) {
+            System.out.println("多边形顶点数量不足或为空");
+            return null;
+        }
+
+        // 创建坐标数组并闭合多边形
+        Coordinate[] shell = new Coordinate[polygonNums.size() + 1];
+        for (int i = 0; i < polygonNums.size(); i++) {
+            double[] polygonD = polygonNums.get(i);
+            shell[i] = new Coordinate(polygonD[0], polygonD[1], 0);
+        }
+        // 闭合多边形
+        shell[polygonNums.size()] = new Coordinate(polygonNums.get(0)[0], polygonNums.get(0)[1], 0);
+
+        // 创建多边形
+        Polygon polygon = geometryFactory.createPolygon(shell);
+
+        System.out.println("polygon:" + polygon);
+
+        Coordinate centro = GeoToolsUtil.getCentro(polygon);
+
+        //返回集合
+        List<Coordinate> retPoint = new ArrayList<>();
+
+        // 循环处理线集合
+        int index = 0;
+        for (LineString line : lineStrings) {
+            index = index + 1;
+            // 计算交集
+            Geometry intersection = polygon.intersection(line);
+
+            // 处理交集结果
+            if (intersection instanceof Point) {
+                System.out.println("交点坐标: " + intersection.getCoordinate());
+            } else if (intersection instanceof MultiPoint) {
+                // 如果有多个交点
+                for (int i = 0; i < intersection.getNumGeometries(); i++) {
+                    Point point = (Point) intersection.getGeometryN(i);
+                    retPoint.add(point.getCoordinate());
+                }
+            } else if (intersection instanceof LineString) {
+                // 交集是一条线段,但这里我们可能不直接打印它作为点
+                System.out.println("交集是一个LineString。");
+                // 如果需要,可以打印线段的起点和终点
+                LineString lineString = (LineString) intersection;
+                Coordinate start = lineString.getStartPoint().getCoordinate();
+                Coordinate end = lineString.getEndPoint().getCoordinate();
+                if (!(index%2==0)) {
+                    retPoint.add(start);
+                    retPoint.add(end);
+                } else {
+                    retPoint.add(end);
+                    retPoint.add(start);
+                }
+
+            } else {
+                // 如果没有交点或结果不是点
+                System.out.println("没有交点或未知类型");
+            }
+        }
+        retPoint.add(centro);
+        return retPoint;
+    }
+
+}
diff --git a/src/main/java/com/dji/sample/wayline/plane/controller/PlaneCreateWayLineController.java b/src/main/java/com/dji/sample/wayline/plane/controller/PlaneCreateWayLineController.java
new file mode 100644
index 0000000..7dc82d8
--- /dev/null
+++ b/src/main/java/com/dji/sample/wayline/plane/controller/PlaneCreateWayLineController.java
@@ -0,0 +1,30 @@
+package com.dji.sample.wayline.plane.controller;
+
+import com.dji.sample.common.model.ResponseResult;
+import com.dji.sample.log.aspect.SysLogAnnotation;
+import com.dji.sample.wayline.plane.BoundingRectangle;
+import com.dji.sample.wayline.plane.MapLatLng;
+import com.dji.sample.wayline.plane.PlaneCourseUtils;
+import com.dji.sample.wayline.plane.param.CreateWaylineParam;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.LineString;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * @Author AIX
+ * @Date 2024/7/19 10:46
+ * @Version 1.0
+ */
+@RestController
+@RequestMapping("/wayline/plane/wayline")
+public class PlaneCreateWayLineController {
+
+    @PostMapping("/createPoints")
+    @SysLogAnnotation(operModul = "创建面状航线", operType = "创建面状航线", operDesc = "创建面状航线")
+    public ResponseResult createPoints(@RequestBody CreateWaylineParam param) {
+        return ResponseResult.success(PlaneCourseUtils.createWaylinePoints(param));
+    }
+
+}
diff --git a/src/main/java/com/dji/sample/wayline/plane/param/CreateWaylineParam.java b/src/main/java/com/dji/sample/wayline/plane/param/CreateWaylineParam.java
new file mode 100644
index 0000000..ff6d30f
--- /dev/null
+++ b/src/main/java/com/dji/sample/wayline/plane/param/CreateWaylineParam.java
@@ -0,0 +1,31 @@
+package com.dji.sample.wayline.plane.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * @Author AIX
+ * @Date 2024/7/19 11:01
+ * @Version 1.0
+ */
+@Data
+public class CreateWaylineParam {
+
+    @NotNull(message = "面不能为空")
+    private List<double[]> polygon;//面
+    @NotNull(message = "高度不能为空")
+    private double height;//高度
+    @NotNull(message = "画幅不能为空")
+    private double frame;//画幅
+    @NotNull(message = "焦距不能为空")
+    private double focal;//焦距
+    @NotNull(message = "航线重叠率不能为空")
+    private double courseRatio;//航向重叠率
+    @NotNull(message = "旁向重叠率不能为空")
+    private double sideRatio;//旁向重叠率
+    @NotNull(message = "航线角度不能为空")
+    private double bearing;//航线角度
+
+}

--
Gitblit v1.9.3