package shop.alien.util.map;

import java.text.DecimalFormat;

/**
 * 两个经纬度距离计算
 *
 * @author ssk
 * @version 1.0
 * @date 2025/3/14 15:39
 */
public class DistanceUtil {

    private static final int EARTH_RADIUS = 6371; // 地球半径，单位为千米

    /**
     * Haversine公式
     * <p>
     * Haversine公式是一种常用的方法，用于计算球面上两个点之间的距离。在此方法中，假设地球是一个完美的球体，忽略了地球的椭球形状。
     *
     * @param lon1 经度1
     * @param lat1 纬度1
     * @param lon2 经度2
     * @param lat2 纬度2
     * @return 两点之间的距离，单位为千米
     */
    public static double haversineCalculateDistance(double lon1, double lat1, double lon2, double lat2) {
        double dLat = Math.toRadians(lat2 - lat1);
        double dLon = Math.toRadians(lon2 - lon1);
        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
                + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
                * Math.sin(dLon / 2) * Math.sin(dLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        DecimalFormat df = new DecimalFormat("#.##");
        return Double.parseDouble(df.format(EARTH_RADIUS * c));
    }

    /**
     * Vincenty公式
     * <p>
     * Vincenty公式是一种更准确的计算方法，可以考虑地球的椭球形状。与Haversine公式相比，Vincenty公式的计算结果更准确，但计算量稍大，适用于更高精度的计算。
     *
     * @param lon1 经度1
     * @param lat1 纬度1
     * @param lon2 经度2
     * @param lat2 纬度2
     * @return 两点之间的距离，单位为千米
     */
    public static double vincentyCalculateDistance(double lon1, double lat1, double lon2, double lat2) {
        double phi1 = Math.toRadians(lat1);
        double lambda1 = Math.toRadians(lon1);
        double phi2 = Math.toRadians(lat2);
        double lambda2 = Math.toRadians(lon2);

        double deltaLambda = Math.abs(lambda1 - lambda2);
        double numerator = Math.sqrt(
                Math.pow(Math.cos(phi2) * Math.sin(deltaLambda), 2) +
                        Math.pow(Math.cos(phi1) * Math.sin(phi2) - Math.sin(phi1) * Math.cos(phi2) * Math.cos(deltaLambda), 2)
        );
        double denominator = Math.sin(phi1) * Math.sin(phi2) + Math.cos(phi1) * Math.cos(phi2) * Math.cos(deltaLambda);
        DecimalFormat df = new DecimalFormat("#.##");
        return Double.parseDouble(df.format(EARTH_RADIUS * Math.atan2(numerator, denominator)));
    }

}