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>> rings = (List>>) coords; for (List> ring : rings) { for (List 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>>> polygons = (List>>>) coords; for (List>> polygon : polygons) { for (List> ring : polygon) { for (List 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 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 toVertexes(FeatureCollection featureCollection) { List 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>> coords = feature.getGeometry().getCoordinates(); if (coords != null && !coords.isEmpty()) { List> outerRing = coords.get(0); for (List point : outerRing) { if (point.size() >= 2) { vertexes.add(new Vertex(point.get(0), point.get(1))); } } } } return vertexes; } }