根據經緯度計算地球兩點之間的距離

前言

之前做過的很多項目中都有涉及到根據經緯度計算兩點之間的距離,然後做排序。這個場景在很多外賣App或者小程序上經常看到,例如距離2km,<100m等等。

工具類1

public class LocationUtil {

    /**
     * 地球半徑,單位 km
     */
    private static final double EARTH_RADIUS = 6371;

    private LocationUtil() {

    }

    /**
     * 根據經緯度,計算兩點間的距離
     *
     * @param longitude1 第一個點的經度
     * @param latitude1  第一個點的緯度
     * @param longitude2 第二個點的經度
     * @param latitude2  第二個點的緯度
     * @return 返回距離 單位米
     */
    public static int getDistance(double longitude1, double latitude1, double longitude2, double latitude2) {
        // 緯度
        double lat1 = Math.toRadians(latitude1);
        double lat2 = Math.toRadians(latitude2);
        // 經度
        double lng1 = Math.toRadians(longitude1);
        double lng2 = Math.toRadians(longitude2);
        // 緯度之差
        double a = lat1 - lat2;
        // 經度之差
        double b = lng1 - lng2;
        // 計算兩點距離的公式
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(b / 2), 2)));
        // 弧長乘地球半徑, 返回單位: 千米
        s = s * EARTH_RADIUS;
        //轉成米
        return (int) Math.round(s * 1000);
    }

    public static void main(String[] args) {
        int d = getDistance(116.353454, 39.996059, 116.308479, 39.983171);
        System.out.println(d);
    }

注意:最後計算的s返回的是km(千米),項目需要可以x1000轉成了m,需要轉成km可以在業務代碼中自己做處理或者交給前端同學去處理均可。

工具類2

public class DistanceUtil {

    private static final double EARTH_RADIUS = 6371;

    private static final String LESS = "<";

    private static final String M = "m";

    private static final String KM = "km";

    private static final int ONE_HUNDRED = 100;

    private static final int THOUSAND_AND_ONE_HUNDRED = 1100;

    private static final int ONE_THOUSAND = 1000;

    private static final BigDecimal BIG_DECIMAL_THOUSAND = new BigDecimal(1000);

    public static final String DISTANCE_DEFAULT_STR = "—";

    /**
     * @param lat1 當前緯度
     * @param lon1 當前經度
     * @param lat2 目標緯度
     * @param lon2 目標經度
     * @return
     */
    public static double distance(double lat1, double lon1, double lat2, double lon2) {
        lat1 = convertDegreesToRadians(lat1);
        lon1 = convertDegreesToRadians(lon1);
        lat2 = convertDegreesToRadians(lat2);
        lon2 = convertDegreesToRadians(lon2);
        double vLat = Math.abs(lat1 - lat2);
        double vLon = Math.abs(lon1 - lon2);
        double h = haverSin(vLat) + Math.cos(lat1) * Math.cos(lat2) * haverSin(vLon);
        return Math.round(2 * EARTH_RADIUS * Math.asin(Math.sqrt(h)) * 1000);
    }

    private static double haverSin(double theta) {
        double v = Math.sin(theta / 2);
        return v * v;
    }

    private static double convertDegreesToRadians(double degrees) {
        return degrees * Math.PI / 180;
    }


    /**
     * 格式化距離
     * @param distance
     * @return
     */
    public static String formatDistance(double distance) {
        String distanceStr = DISTANCE_DEFAULT_STR;
        if (ObjectUtil.isNull(distance) || distance <= -1) {
            return distanceStr;
        }
        if (distance < ONE_HUNDRED) {
            distanceStr = LESS + ONE_HUNDRED + M;
        } else if (distance >= ONE_HUNDRED && distance < ONE_THOUSAND) {
            distanceStr = new BigDecimal(distance).setScale(0, BigDecimal.ROUND_DOWN).toString();
            distanceStr = distanceStr + M;
        } else if (distance >= ONE_THOUSAND) {
            distanceStr = new BigDecimal(distance).divide(BIG_DECIMAL_THOUSAND).setScale(1, BigDecimal.ROUND_DOWN).toString();
            distanceStr = distanceStr + KM;
        }
        return distanceStr;
    }
}

注意:這和第一種的計算方式其實是一樣的,只是用的形式不一樣,可以自定義格式化一些前端需要的數據格式。

 

Demo

private List<AddressInfoModel> buildResultList(AddressListParam param, List<QrorderingCustomerAddressDO> list) {
        Float longitude = null;
        Float latitude = null;
        this.fillParam(param, longitude, latitude);
        List<AddressInfoModel> resultList = Lists.newArrayList();
        AddressInfoModel addressInfoModel;
        for (QrorderingCustomerAddressDO addressDO : list) {
            addressInfoModel = FsBeanUtil.map(addressDO, AddressInfoModel.class);
            int distance = LocationUtil.getDistance(Double.valueOf(longitude), Double.valueOf(latitude), Double.valueOf(addressDO.getLongitude()), Double.valueOf(addressDO.getLatitude()));
            addressInfoModel.setDistance(distance);
            if (distance > param.getDeliveryArea()) {
                addressInfoModel.setDistributionScope(CommonConstant.CONSTANT_ONE);
            } else {
                addressInfoModel.setDistributionScope(CommonConstant.CONSTANT_TWO);
            }
            resultList.add(addressInfoModel);
        }
        //按照距離降序排序
        resultList.sort(Comparator.comparingInt(o -> o.getDistance()));
        return resultList;
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章