|
@@ -0,0 +1,121 @@
|
|
|
+package com.leromro.common.utils;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+public class GeoUtils {
|
|
|
+
|
|
|
+ private static final int EARTH_RADIUS_METERS = 6371000; // 地球半径(米)
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算两点之间的距离(Haversine公式)
|
|
|
+ *
|
|
|
+ * @param lat1 纬度1
|
|
|
+ * @param lon1 经度1
|
|
|
+ * @param lat2 纬度2
|
|
|
+ * @param lon2 经度2
|
|
|
+ * @return 距离(米)
|
|
|
+ */
|
|
|
+ public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
|
|
|
+ double latDistance = Math.toRadians(lat2 - lat1);
|
|
|
+ double lonDistance = Math.toRadians(lon2 - lon1);
|
|
|
+ double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
|
|
|
+ + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
|
|
|
+ * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
|
|
|
+ double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
|
+ return EARTH_RADIUS_METERS * c;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断一个点是否在圆形围栏内
|
|
|
+ *
|
|
|
+ * @param pointLat 点纬度
|
|
|
+ * @param pointLon 点经度
|
|
|
+ * @param centerLat 围栏中心纬度
|
|
|
+ * @param centerLon 围栏中心经度
|
|
|
+ * @param radiusMeters 围栏半径(米)
|
|
|
+ * @return 是否在围栏内
|
|
|
+ */
|
|
|
+ public static boolean isInsideCircle(double pointLat, double pointLon, double centerLat, double centerLon, double radiusMeters) {
|
|
|
+ double distance = calculateDistance(pointLat, pointLon, centerLat, centerLon);
|
|
|
+ return distance <= radiusMeters;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断一个点是否在多边形围栏内(适用于任意多边形)
|
|
|
+ *
|
|
|
+ * @param pointLat 点纬度
|
|
|
+ * @param pointLon 点经度
|
|
|
+ * @param polygon 多边形顶点列表(格式为 [lat1, lon1, lat2, lon2, ...])
|
|
|
+ * @return 是否在多边形内
|
|
|
+ */
|
|
|
+ public static boolean isInsidePolygon(double pointLat, double pointLon, List<double[]> polygon) {
|
|
|
+ int n = polygon.size();
|
|
|
+ boolean inside = false;
|
|
|
+ for (int i = 0, j = n - 1; i < n; j = i++) {
|
|
|
+ double xi = polygon.get(i)[0], yi = polygon.get(i)[1];
|
|
|
+ double xj = polygon.get(j)[0], yj = polygon.get(j)[1];
|
|
|
+
|
|
|
+ boolean intersect = ((yi > pointLon) != (yj > pointLon))
|
|
|
+ && (pointLat < (xj - xi) * (pointLon - yi) / (yj - yi) + xi);
|
|
|
+ if (intersect) inside = !inside;
|
|
|
+ }
|
|
|
+ return inside;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断一个点是否在多边形或圆形围栏内
|
|
|
+ *
|
|
|
+ * @param pointLat 点纬度
|
|
|
+ * @param pointLon 点经度
|
|
|
+ * @param fenceType 类型:"circle" 或 "polygon"
|
|
|
+ * @param args 可变参数:
|
|
|
+ * - 如果是 circle: centerLat, centerLon, radiusMeters
|
|
|
+ * - 如果是 polygon: List<double[]> polygon
|
|
|
+ * @return 是否在围栏内
|
|
|
+ */
|
|
|
+ public static boolean isInFence(double pointLat, double pointLon, String fenceType, Object... args) {
|
|
|
+ switch (fenceType.toLowerCase()) {
|
|
|
+ case "circle":
|
|
|
+ if (args.length == 3 && args[0] instanceof Double && args[1] instanceof Double && args[2] instanceof Double) {
|
|
|
+ double centerLat = (Double) args[0];
|
|
|
+ double centerLon = (Double) args[1];
|
|
|
+ double radius = (Double) args[2];
|
|
|
+ return isInsideCircle(pointLat, pointLon, centerLat, centerLon, radius);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case "polygon":
|
|
|
+ if (args.length == 1 && args[0] instanceof List<?>) {
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ List<double[]> polygon = (List<double[]>) args[0];
|
|
|
+ return isInsidePolygon(pointLat, pointLon, polygon);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new IllegalArgumentException("Unsupported fence type: " + fenceType);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 两点间距离
|
|
|
+ double distance = GeoUtils.calculateDistance(39.9042, 116.4074, 31.2304, 121.4737); // 北京到上海
|
|
|
+ System.out.println("Distance: " + distance + " meters");
|
|
|
+
|
|
|
+ // 点是否在圆形内
|
|
|
+ boolean inCircle = GeoUtils.isInsideCircle(39.905, 116.408, 39.9042, 116.4074, 1000); // 半径1000米
|
|
|
+ System.out.println("In Circle: " + inCircle);
|
|
|
+
|
|
|
+ // 点是否在多边形内
|
|
|
+ List<double[]> polygon = Arrays.asList(
|
|
|
+ new double[]{39.904, 116.407},
|
|
|
+ new double[]{39.905, 116.407},
|
|
|
+ new double[]{39.905, 116.408},
|
|
|
+ new double[]{39.904, 116.408}
|
|
|
+ );
|
|
|
+
|
|
|
+ boolean inPolygon = GeoUtils.isInsidePolygon(39.9045, 116.4075, polygon);
|
|
|
+ System.out.println("In Polygon: " + inPolygon);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|