All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.biz.common.utils.RangeUtils Maven / Gradle / Ivy

There is a newer version: 1.5.1
Show newest version
package com.biz.common.utils;

/**
 * 该类提供了一些关于经纬度范围的计算工具方法。
 * 主要用于根据给定的经纬度和距离,计算出扩展后的经纬度范围。
 *
 * @author francis
 * @create 2023/3/31 17:36
 */
public final class RangeUtils {

    /**
     * WGS84椭球体的长半轴。
     */
    private static final double EARTH_MAJOR_AXIS = 6378.137;

    /**
     * WGS84椭球体的短半轴。
     */
    private static final double EARTH_MINOR_AXIS = 6356752.3142;

    /**
     * WGS84椭球体的扁率。
     */
    private static final double EARTH_FLATTENING = 1 / 298.2572236;

    /**
     * 根据给定的经纬度和距离,计算左下角的经纬度点。
     * 该方法主要用于地图的矩形区域的左下角计算。
     *
     * @param longitude 经度
     * @param latitude  纬度
     * @return 左下角的经纬度数组,顺序为[经度,纬度]
     */
    public static double[] leftDownPoint(float longitude, float latitude) {
        if (!isValidLngLat((double) longitude, (double) latitude)) {
            throw new IllegalArgumentException("Invalid longitude or latitude values.");
        }
        return rangeOutsidePointByLongitudeAndLatitude(longitude, latitude, 225, 1000);
    }


    /**
     * 根据给定的经纬度和距离,计算右上角的经纬度点。
     * 该方法主要用于地图的矩形区域的右上角计算。
     *
     * @param longitude 经度
     * @param latitude  纬度
     * @return 右上角的经纬度数组,顺序为[经度,纬度]
     */
    public static double[] rightUpPoint(float longitude, float latitude) {
        if (!isValidLngLat((double) longitude, (double) latitude)) {
            throw new IllegalArgumentException("Invalid longitude or latitude values.");
        }
        return rangeOutsidePointByLongitudeAndLatitude(longitude, latitude, 45, 1000);
    }


    /**
     * 根据经度、纬度、角度和距离,计算目标点的经纬度。
     * 该方法主要用于计算在给定角度和距离下,从起点经纬度出发的目标点的经纬度。
     *
     * @param longitude 起点经度
     * @param latitude  起点纬度
     * @param degree    方向角度,正北为0度,顺时针增加
     * @param dist      距离,单位为米
     * @return 目标点的经纬度数组,顺序为[经度,纬度]
     */
    public static double[] rangeOutsidePointByLongitudeAndLatitude(double longitude, double latitude, double degree, double dist) {
        return rangeOutsidePoint(longitude, latitude, rad(degree), dist);
    }


    /**
     * 根据弧度、经纬度和距离,计算目标点的经纬度。
     * 这是rangeOutsidePointByLongitudeAndLatitude的内部实现方法,用于实际的计算过程。
     *
     * @param longitude 起点经度
     * @param latitude  起点纬度
     * @param radian    方向弧度
     * @param dist      距离,单位为米
     * @return 目标点的经纬度数组,顺序为[经度,纬度]
     */
    private static double[] rangeOutsidePoint(double longitude, double latitude, double radian, double dist) {
        double sinAlpha1 = Math.sin(radian);
        double cosAlpha1 = Math.cos(radian);
        double tanU1 = (1 - EARTH_FLATTENING) * Math.tan(rad(latitude));
        double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1));
        double sinU1 = tanU1 * cosU1;
        double sigma1 = Math.atan2(tanU1, cosAlpha1);
        double sinAlpha = cosU1 * sinAlpha1;
        double cosSqAlpha = 1 - sinAlpha * sinAlpha;
        double uSq = cosSqAlpha * (EARTH_MAJOR_AXIS * EARTH_MAJOR_AXIS - EARTH_MINOR_AXIS * EARTH_MINOR_AXIS) / (EARTH_MINOR_AXIS * EARTH_MINOR_AXIS);
        double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
        double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
        double cos2SigmaM = 0;
        double sinSigma = 0;
        double cosSigma = 0;
        double sigma = dist / (EARTH_MINOR_AXIS * A), sigmaP = 2 * Math.PI;
        while (Math.abs(sigma - sigmaP) > 1e-12) {
            cos2SigmaM = Math.cos(2 * sigma1 + sigma);
            sinSigma = Math.sin(sigma);
            cosSigma = Math.cos(sigma);
            double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)
                    - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
            sigmaP = sigma;
            sigma = dist / (EARTH_MINOR_AXIS * A) + deltaSigma;
        }

        double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
        double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
                (1 - EARTH_FLATTENING) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
        double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
        double C = EARTH_FLATTENING / 16 * cosSqAlpha * (4 + EARTH_FLATTENING * (4 - 3 * cosSqAlpha));
        double L = lambda - (1 - C) * EARTH_FLATTENING * sinAlpha
                * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
        return new double[]{longitude + deg(L), lat2};
    }


    /**
     * 将角度转换为弧度。
     * 

* 弧度和角度之间的转换是数学中常见的需求。这个方法提供了一个简单的途径, * 将给定的角度值转换为对应的弧度值。在数学计算中,弧度常常比角度更方便使用。 * * @param d 输入的角度值,单位为度。 * @return 转换后的弧度值。 */ private static double rad(double d) { return d * Math.PI / 180.0; } /** * 将弧度转换为度数。 *

* 由于地球经纬度的度量单位是度,而某些计算可能使用弧度制,这个函数提供了弧度到度的转换。 * 它是数学中常见的一种单位转换,尤其在几何和三角学中。 * * @param x 弧度值。 * @return 对应的度数。 */ private static double deg(double x) { return x * 180 / Math.PI; } /** * 判断给定的经纬度是否有效。 *

* 在地理信息系统(GIS)中,有效的经纬度必须在特定的范围内。 * 这个函数用于验证经度是否在1到180之间,纬度是否在1到90之间。 * 这是因为经度的范围是180度,从西经180度到东经180度,而纬度的范围是90度,从南纬90度到北纬90度。 * * @param longitude 经度值。 * @param latitude 纬度值。 * @return 如果经纬度有效,则返回true;否则返回false。 */ public static boolean isValidLngLat(Double longitude, Double latitude) { return longitude != null && latitude != null && longitude < 180 && latitude < 90 && longitude > 1 && latitude > 1; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy