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