工具类-根据经纬度计算两点之间在地图上的距离和方向

文章目录


前言

通过经纬度计算地图上两个点的距离和方向,有需要的自取。

工具类

import cn.hutool.core.util.ObjectUtil;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import static java.lang.Math.*;
import static java.lang.Math.atan2;

/**
 * 坐标工具类
 *
 * @author qf
 */
public class CoordinateUtil {
    public static void main(String[] args) {
        double distance = getDistance(116.37047010, 39.83594606, 116.42088584, 39.87221456);
        System.out.println("两者之间的距离为:"+distance);
        Address a = new Address();
        a.name = "a";
        a.lon = 116.37047010;
        a.lat = 39.83594606;
        Address b = new Address();
        b.name = "b";
        b.lon = 116.42088584;
        b.lat = 39.87221456;
        Map<String, Object> map = getDistanceAndDirection(a.lon, a.lat, b.lon, b.lat);
        System.out.println("两者之间的距离为:"+map.get("distance"));
        System.out.println("b 在 a 的方向为:"+map.get("direction"));
    }

    public static class Address{
        private String name;
        private double lon;
        private double lat;
    }

    //WGS84坐标系,长轴6378137.000m,短轴6356752.314,扁率1/298.257223563。
    public static final double f = 1 /  298.257223563;
    public static final double a= 6378137.0;
    public static final double b= 6356752.314245;

    /**
     * 调整给定的角度值 angle,使其保持在0到360度之间
     * @param angle
     * @return
     */
    public static double judge (double angle){
        if (angle < 0) {
            return angle + 360;
        } else {
            return angle;
        }
    }

    /**
     * 获得两个点之间的距离(单位:km)和相对角度
     * @param lon1
     * @param lat1
     * @param lon2
     * @param lat2
     * @return 两个点之间的距离(单位:km)和相对角度
     */
    public static Map<String,Double> getDistanceAndBearing(double lon1, double lat1, double lon2, double lat2) {
        //将经纬度坐标转换为弧度
        lon1 = Math.toRadians(lon1);
        lat1 = Math.toRadians(lat1);
        lon2 = Math.toRadians(lon2);
        lat2 = Math.toRadians(lat2);

        double L = lon2 - lon1;
        // 增加一个微小值到L以避免分母(两点的纬度相同)为0的情况
        if (L == 0) {
            L += 1.7453292588953673E-8; // 防止lon1和lon2相等时出现除以零的情况
        }

        double tanU1 = (1 - f) * tan(lat1);
        double cosU1 = 1 / sqrt((1 + tanU1 * tanU1));
        double sinU1 = tanU1 * cosU1;

        double tanU2 = (1 - f) * tan(lat2);
        double cosU2 = 1 / sqrt((1 + tanU2 * tanU2));
        double sinU2 = tanU2 * cosU2;

        double lambda = L;
        double lambda_ = 0;
        double iterationLimit = 100;

        double cosSq_alpha = 0;

        double sin_delta = 0;
        double cos2_deltaM = 0;
        double cos_delta = 0;
        double delta = 0;
        double sinlambda = 0;
        double coslambda = 0;
        double s = 0;

        while (abs(lambda - lambda_) > 1e-12 && iterationLimit > 0) {
            iterationLimit = iterationLimit - 1;
            sinlambda = sin(lambda);
            coslambda = cos(lambda);
            double sinSq_delta = (cosU2 * sinlambda) * (cosU2 * sinlambda) + (cosU1 * sinU2 - sinU1 * cosU2 * coslambda) * (cosU1 * sinU2 - sinU1 * cosU2 * coslambda);
            sin_delta = sqrt(sinSq_delta);
            if (sin_delta == 0) {

            }
            cos_delta = sinU1 * sinU2 + cosU1 * cosU2 * coslambda;
            delta = atan2(sin_delta, cos_delta);
            double sin_alpha = cosU1 * cosU2 * sinlambda / sin_delta;
            cosSq_alpha = 1 - sin_alpha * sin_alpha;
            cos2_deltaM = cos_delta - 2 * sinU1 * sinU2 / cosSq_alpha;
            double C = f / 16 * cosSq_alpha * (4 + f * (4 - 3 * cosSq_alpha));
            lambda_ = lambda;
            lambda = L + (1 - C) * f * sin_alpha * (delta + C * sin_delta * (cos2_deltaM + C * cos_delta * (-1 + 2 * cos2_deltaM * cos2_deltaM)));
        }

        double uSq = cosSq_alpha * (a * a - b * b) / (b * b);
        double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
        double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
        double delta_delta = B * sin_delta * (cos2_deltaM + B / 4 * (cos_delta * (-1 + 2 * cos2_deltaM * cos2_deltaM) - B / 6 * cos2_deltaM * (-3 + 4 * sin_delta * sin_delta) * (-3 + 4 * cos2_deltaM * cos2_deltaM)));
        s = b * A * (delta - delta_delta);                                        //距离m
        double fwdAz = atan2(cosU2 * sinlambda, cosU1 * sinU2 - sinU1 * cosU2 * coslambda); //初始方位角
        double revAz = atan2(cosU1 * sinlambda, -sinU1 * cosU2 + cosU1 * sinU2 * coslambda); //最终方位角
        double fwdAz_ = judge(Math.toDegrees(fwdAz));  //将角度归一化至0 - 360°内
        double revAz_ = judge(Math.toDegrees(revAz));  //将角度归一化至0 - 360°内
        s = s / 1000;  //距离km

        HashMap map = new HashMap();
        //初始方位角
        map.put("initialAzimuth",fwdAz_);
        //最终方位角
        map.put("finalAzimuth",revAz_);
        //两点相距 距离km
        map.put("distance",s);

        return map;
    }

    /**
     * 获得两个点之间的距离(单位:m)
     * @param lon1
     * @param lat1
     * @param lon2
     * @param lat2
     * @return
     */
    public static double getDistance(double lon1, double lat1, double lon2, double lat2) {
        //将经纬度坐标转换为弧度
        lon1 = Math.toRadians(lon1);
        lat1 = Math.toRadians(lat1);
        lon2 = Math.toRadians(lon2);
        lat2 = Math.toRadians(lat2);

        // 检查经度是否相等(防止lon1和lon2相等时出现除以零的情况)
        double L = lon2 - lon1;
        // 增加一个微小值到L以避免分母为0的情况
        if (L == 0) {
            L += 1.7453292588953673E-8; // 防止lon1和lon2相等时出现除以零的情况
        }

        double tanU1 = (1 - f) * tan(lat1);
        double cosU1 = 1 / sqrt((1 + tanU1 * tanU1));
        double sinU1 = tanU1 * cosU1;

        double tanU2 = (1 - f) * tan(lat2);
        double cosU2 = 1 / sqrt((1 + tanU2 * tanU2));
        double sinU2 = tanU2 * cosU2;

        double lambda = L;
        double lambda_ = 0;
        double iterationLimit = 100;

        double cosSq_alpha = 0;

        double sin_delta = 0;
        double cos2_deltaM = 0;
        double cos_delta = 0;
        double delta = 0;
        double sinlambda = 0;
        double coslambda = 0;
        double s = 0;

        while (abs(lambda - lambda_) > 1e-12 && iterationLimit > 0) {
            iterationLimit = iterationLimit - 1;
            sinlambda = sin(lambda);
            coslambda = cos(lambda);
            double sinSq_delta = (cosU2 * sinlambda) * (cosU2 * sinlambda) + (cosU1 * sinU2 - sinU1 * cosU2 * coslambda) * (cosU1 * sinU2 - sinU1 * cosU2 * coslambda);
            sin_delta = sqrt(sinSq_delta);
            if (sin_delta == 0) {

            }
            cos_delta = sinU1 * sinU2 + cosU1 * cosU2 * coslambda;
            delta = atan2(sin_delta, cos_delta);
            double sin_alpha = cosU1 * cosU2 * sinlambda / sin_delta;
            cosSq_alpha = 1 - sin_alpha * sin_alpha;
            cos2_deltaM = cos_delta - 2 * sinU1 * sinU2 / cosSq_alpha;
            double C = f / 16 * cosSq_alpha * (4 + f * (4 - 3 * cosSq_alpha));
            lambda_ = lambda;
            lambda = L + (1 - C) * f * sin_alpha * (delta + C * sin_delta * (cos2_deltaM + C * cos_delta * (-1 + 2 * cos2_deltaM * cos2_deltaM)));
        }

        double uSq = cosSq_alpha * (a * a - b * b) / (b * b);
        double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
        double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
        double delta_delta = B * sin_delta * (cos2_deltaM + B / 4 * (cos_delta * (-1 + 2 * cos2_deltaM * cos2_deltaM) - B / 6 * cos2_deltaM * (-3 + 4 * sin_delta * sin_delta) * (-3 + 4 * cos2_deltaM * cos2_deltaM)));
        s = b * A * (delta - delta_delta);                                        //距离m
        double fwdAz = atan2(cosU2 * sinlambda, cosU1 * sinU2 - sinU1 * cosU2 * coslambda); //初始方位角
        double revAz = atan2(cosU1 * sinlambda, -sinU1 * cosU2 + cosU1 * sinU2 * coslambda); //最终方位角
        double fwdAz_ = judge(Math.toDegrees(fwdAz));  //将角度归一化至0 - 360°内
        double revAz_ = judge(Math.toDegrees(revAz));  //将角度归一化至0 - 360°内
        return s;
    }

    /**
     * 获得两个点之间的距离(单位:m)和方向(2在1的相对方向)
     * @param lon1
     * @param lat1
     * @param lon2
     * @param lat2
     * @return
     */
    public static Map<String,Object> getDistanceAndDirection(double lon1, double lat1, double lon2, double lat2) {
        Map<String, Double> map = getDistanceAndBearing(lon1, lat1, lon2, lat2);
        Double finalAzimuth = map.get("finalAzimuth");
        BigDecimal bigDecimal = new BigDecimal(finalAzimuth).setScale(0, BigDecimal.ROUND_HALF_UP);
        String direction = getNameByDirection(bigDecimal.intValue());
        Map<String, Object> mapFinal = new HashMap<>();
        mapFinal.put("finalAzimuth",finalAzimuth);//最终方位角
        mapFinal.put("direction",direction);//方向
        mapFinal.put("distance",map.get("distance") * 1000);//距离
        return mapFinal;
    }

    /**
     * 相对角度转换为方向
     * @param direction
     * @return
     */
    private static String getNameByDirection(Integer direction) {
        if(direction == null){
            return null;
        }
        if((direction >= 338 && direction <= 360) || direction >= 0 && direction <= 22){
            return "北";
        } else if (direction >= 23 && direction <= 67) {
            return "东北";
        }else if (direction >= 68 && direction <= 112) {
            return "东";
        }else if (direction >= 113 && direction <= 157) {
            return "东南";
        } else if (direction >= 158 && direction <= 202) {
            return "南";
        }else if (direction >= 203 && direction <= 247) {
            return "西南";
        }else if (direction >= 248 && direction <= 292) {
            return "西";
        }else if (direction >= 293 && direction <= 337) {
            return "西北";
        }
        return "风向异常";
    }


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值