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 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 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 createPoint(MapLatLng mapLatLng, int intValue, double bearing, MapLatLng mapLatLngMin) { List 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 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 topLeftPointList = PlaneCourseUtils.createPoint(boundingRectangle.topLeft, intValue, param.getBearing(), mapLatLngMin); List topRightPointList = PlaneCourseUtils.createPoint(boundingRectangle.topRight, intValue, param.getBearing(), mapLatLngMin); List lineStrings = PlaneCourseUtils.createLine(topLeftPointList, topRightPointList); List 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 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 topLeftPointList = createPoint(boundingRectangle.topLeft, intValue, bearing, mapLatLngMin); List topRightPointList = createPoint(boundingRectangle.topRight, intValue, bearing, mapLatLngMin); List lineStrings = createLine(topLeftPointList, topRightPointList); List points = getPoints(polygon, lineStrings); for (Coordinate point:points) { System.out.println("" + point.x + ", " + point.y + ";"); } } /** * 创建线的集合 * * @param topLeftPointList 左上角点集合 * @param topRightPointList 右上角点集合 */ private static List createLine(List topLeftPointList, List topRightPointList) { // 创建GeometryFactory GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(); //线的集合 List 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 getPoints(List polygonNums, List 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 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; } }