| New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | } |