| | |
| | | |
| | | public class NoFlyZoneUtils { |
| | | |
| | | |
| | | /** |
| | | * 计算点到多边形的最近距离(米) |
| | | */ |
| | | public static double pointToPolygonDistance(Vertex point, List<Vertex> polygon) { |
| | | |
| | | // 如果在多边形内部,距离为 0 |
| | | if (isPointInPolygon(point, polygon)) { |
| | | return 0; |
| | | } |
| | | |
| | | double minDistance = Double.MAX_VALUE; |
| | | int n = polygon.size(); |
| | | |
| | | for (int i = 0; i < n; i++) { |
| | | Vertex a = polygon.get(i); |
| | | Vertex b = polygon.get((i + 1) % n); // 下一点,最后一条边连接第一个点 |
| | | |
| | | double dist = pointToSegmentDistanceHaversine(point, a, b); |
| | | minDistance = Math.min(minDistance, dist); |
| | | } |
| | | |
| | | return minDistance; |
| | | } |
| | | |
| | | /** |
| | | * 计算点到线段的距离(米)使用Haversine公式 |
| | | */ |
| | | public static double pointToSegmentDistanceHaversine(Vertex p, Vertex a, Vertex b) { |
| | | // 将经纬度换算成以 p 点为参考的局部平面米坐标 |
| | | double[] pa = latLonToXY(a, p.y, p.x); |
| | | double[] pb = latLonToXY(b, p.y, p.x); |
| | | double[] pp = latLonToXY(p, p.y, p.x); |
| | | |
| | | double dx = pb[0] - pa[0]; |
| | | double dy = pb[1] - pa[1]; |
| | | |
| | | double t = ((pp[0] - pa[0]) * dx + (pp[1] - pa[1]) * dy) / (dx * dx + dy * dy); |
| | | |
| | | double nearestX, nearestY; |
| | | if (t < 0) { |
| | | nearestX = pa[0]; |
| | | nearestY = pa[1]; |
| | | } else if (t > 1) { |
| | | nearestX = pb[0]; |
| | | nearestY = pb[1]; |
| | | } else { |
| | | nearestX = pa[0] + t * dx; |
| | | nearestY = pa[1] + t * dy; |
| | | } |
| | | |
| | | double distX = pp[0] - nearestX; |
| | | double distY = pp[1] - nearestY; |
| | | |
| | | return Math.sqrt(distX * distX + distY * distY); |
| | | } |
| | | |
| | | /** |
| | | * 将经纬度转换为局部平面坐标(米) |
| | | * originLat、originLon 是参考点,经纬度转换到以参考点为原点的平面坐标 |
| | | */ |
| | | public static double[] latLonToXY(Vertex point, double originLat, double originLon) { |
| | | double R = 6371000; // 地球半径,单位米 |
| | | double dLat = Math.toRadians(point.y - originLat); |
| | | double dLon = Math.toRadians(point.x - originLon); |
| | | double x = R * dLon * Math.cos(Math.toRadians(originLat)); |
| | | double y = R * dLat; |
| | | return new double[]{x, 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); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 判断点是否在矩形内 |
| | | */ |
| | | public static boolean isInsideBox(Vertex point, ArdNoFlyBox box) { |
| | | if (point == null || box == null) return false; |
| | | return point.x >= box.getMinx() && |
| | | point.x <= box.getMaxx() && |
| | | point.y >= box.getMiny() && |
| | | point.y <= box.getMaxy(); |
| | | } |
| | | |
| | | |
| | |
| | | return vertexes; |
| | | } |
| | | |
| | | |
| | | public static double degDistanceToMeter(double deg, double lat) { |
| | | // 1° ≈ 111320 米,但是经度和纬度不一样,这里用纬度做修正 |
| | | return deg * 111320.0 * Math.cos(Math.toRadians(lat)); |
| | | } |
| | | } |