浏览代码

Merge remote-tracking branch 'origin/master'

LiRong 1 周之前
父节点
当前提交
d88be7a270
共有 1 个文件被更改,包括 121 次插入0 次删除
  1. 121 0
      leromro-common/src/main/java/com/leromro/common/utils/GeoUtils.java

+ 121 - 0
leromro-common/src/main/java/com/leromro/common/utils/GeoUtils.java

@@ -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);
+    }
+
+}