zhaowenyu
2026-04-01 b8b5a3895e741d5348ecdd39ad2e5cbd443f8e89
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package com.ard.sample.annotation.util;
 
import lombok.extern.slf4j.Slf4j;
import java.util.List;
 
/**
 * 地理坐标计算工具类(WGS84坐标系)
 * 支持:
 * 1. 线:水平距离(累加)、直线距离(起点→终点)
 * 2. 面:水平面积、水平周长
 * 3. 圆:水平面积(πr²)、水平周长(2πr)
 */
@Slf4j
public class GeoCalculator {
    // WGS84椭球参数
    private static final double WGS84_A = 6378137.0; // 地球长半轴(米)
    private static final double RAD = Math.PI / 180.0; // 角度转弧度
    private static final double PI = Math.PI; // 圆周率
 
    // ====================== 通用工具方法 ======================
    /**
     * 保留两位小数(四舍五入)
     */
    public static double keepTwoDecimal(double value) {
        return Math.round(value * 100.0) / 100.0;
    }
 
    /**
     * 计算两点间的水平距离(米)- 经纬度投影到水平面
     */
    private static double calculateTwoPointHorizontalDistance(Coordinate p1, Coordinate p2) {
        if (p1 == null || p2 == null) return 0.0;
        // 转换为弧度
        double lon1 = p1.getLongitude() * RAD;
        double lat1 = p1.getLatitude() * RAD;
        double lon2 = p2.getLongitude() * RAD;
        double lat2 = p2.getLatitude() * RAD;
 
        // 计算纬度对应的地球切面半径
        double R = WGS84_A * Math.cos(lat1);
        // 经度差对应的水平距离
        double dx = R * (lon2 - lon1);
        // 纬度差对应的水平距离
        double dy = WGS84_A * (lat2 - lat1);
        // 水平直线距离(勾股定理)
        return Math.sqrt(dx * dx + dy * dy);
    }
 
    /**
     * 计算两点间的直线地理距离(米)- Haversine公式
     */
    private static double calculateTwoPointStraightDistance(Coordinate p1, Coordinate p2) {
        if (p1 == null || p2 == null) return 0.0;
        double lon1 = p1.getLongitude() * RAD;
        double lat1 = p1.getLatitude() * RAD;
        double lon2 = p2.getLongitude() * RAD;
        double lat2 = p2.getLatitude() * RAD;
 
        double deltaLat = lat2 - lat1;
        double deltaLon = lon2 - lon1;
 
        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2)
                + Math.cos(lat1) * Math.cos(lat2)
                * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return WGS84_A * c;
    }
 
    // ====================== 线(折线)计算 ======================
    /**
     * 计算折线的水平距离(累加,米)
     */
    public static double calculatePolylineHorizontalDistance(List<Coordinate> points) {
        if (points == null || points.size() < 2) {
            log.warn("折线水平距离计算失败:顶点数不足2个");
            return 0.0;
        }
        double totalHorizontal = 0.0;
        for (int i = 0; i < points.size() - 1; i++) {
            totalHorizontal += calculateTwoPointHorizontalDistance(points.get(i), points.get(i + 1));
        }
        return keepTwoDecimal(totalHorizontal);
    }
 
    /**
     * 计算折线的直线距离(起点→终点,米)
     */
    public static double calculatePolylineStraightDistance(List<Coordinate> points) {
        if (points == null || points.size() < 2) {
            log.warn("折线直线距离计算失败:顶点数不足2个");
            return 0.0;
        }
        Coordinate start = points.get(0);
        Coordinate end = points.get(points.size() - 1);
        return keepTwoDecimal(calculateTwoPointStraightDistance(start, end));
    }
 
    // ====================== 面(多边形)计算 ======================
    /**
     * 计算多边形的水平面积(平方米)
     */
    public static double calculatePolygonHorizontalArea(List<Coordinate> points) {
        if (points == null || points.size() < 3) {
            log.warn("多边形水平面积计算失败:顶点数不足3个");
            return 0.0;
        }
        // 闭合多边形
        List<Coordinate> closedPoints = new java.util.ArrayList<>(points);
        Coordinate first = closedPoints.get(0);
        Coordinate last = closedPoints.get(closedPoints.size() - 1);
        if (!isCoordinateEqual(first, last)) {
            closedPoints.add(first);
        }
 
        double area = 0.0;
        int n = closedPoints.size() - 1;
        for (int i = 0; i < n; i++) {
            Coordinate p1 = closedPoints.get(i);
            Coordinate p2 = closedPoints.get(i + 1);
            // 水平投影面积公式(高斯平面投影)
            area += (p1.getLongitude() * p2.getLatitude() - p2.getLongitude() * p1.getLatitude());
        }
        // 转换为实际面积(修正系数)
        area = Math.abs(area) * 0.5 * WGS84_A * WGS84_A * RAD * RAD;
        return keepTwoDecimal(area);
    }
 
    /**
     * 计算多边形的水平周长(米)
     */
    public static double calculatePolygonHorizontalPerimeter(List<Coordinate> points) {
        if (points == null || points.size() < 3) {
            log.warn("多边形水平周长计算失败:顶点数不足3个");
            return 0.0;
        }
        // 闭合多边形
        List<Coordinate> closedPoints = new java.util.ArrayList<>(points);
        Coordinate first = closedPoints.get(0);
        Coordinate last = closedPoints.get(closedPoints.size() - 1);
        if (!isCoordinateEqual(first, last)) {
            closedPoints.add(first);
        }
 
        double perimeter = 0.0;
        int n = closedPoints.size() - 1;
        for (int i = 0; i < n; i++) {
            perimeter += calculateTwoPointHorizontalDistance(closedPoints.get(i), closedPoints.get(i + 1));
        }
        return keepTwoDecimal(perimeter);
    }
 
    // ====================== 圆计算 ======================
    /**
     * 计算圆的水平面积(平方米)- π×r²
     */
    public static double calculateCircleHorizontalArea(double radius) {
        if (radius <= 0) {
            log.warn("圆形水平面积计算失败:半径非法({}米)", radius);
            return 0.0;
        }
        return keepTwoDecimal(PI * radius * radius);
    }
 
    /**
     * 计算圆的水平周长(米)- 2πr
     */
    public static double calculateCircleHorizontalPerimeter(double radius) {
        if (radius <= 0) {
            log.warn("圆形水平周长计算失败:半径非法({}米)", radius);
            return 0.0;
        }
        return keepTwoDecimal(2 * PI * radius);
    }
 
    // ====================== 辅助方法 ======================
    /**
     * 判断两个坐标是否相等(容错1e-6)
     */
    private static boolean isCoordinateEqual(Coordinate p1, Coordinate p2) {
        if (p1 == null || p2 == null) return false;
        double lonDiff = Math.abs(p1.getLongitude() - p2.getLongitude());
        double latDiff = Math.abs(p1.getLatitude() - p2.getLatitude());
        return lonDiff < 1e-6 && latDiff < 1e-6;
    }
 
    // ====================== 坐标实体类 ======================
    public static class Coordinate {
        private double longitude; // 经度
        private double latitude;  // 纬度
 
        public Coordinate() {}
        public Coordinate(double longitude, double latitude) {
            this.longitude = longitude;
            this.latitude = latitude;
        }
 
        // Getter & Setter
        public double getLongitude() { return longitude; }
        public void setLongitude(double longitude) { this.longitude = longitude; }
        public double getLatitude() { return latitude; }
        public void setLatitude(double latitude) { this.latitude = latitude; }
    }
}