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