java 根据两个地点的经纬度计算两点间距离

科普

地球是一个近乎标准的椭球体,它的赤道半径为6378.140千米,极半径为 6356.755千米,平均半径6371.004千米。如果我们假设地球是一个完美的球体,那么它的半径就是地球的平均半径,记为R。

如果以0度经线为基 准,那么根据地球表面任意两点的经纬度就可以计算出这两点间的地表距离(这里忽略地球表面地形对计算带来的误差,仅仅是理论上的估算值)。

设第一点A的经 纬度为(LonA, LatA),第二点B的经纬度为(LonB, LatB),按照0度经线的基准,东经取经度的正值(Longitude),西经取经度负值(-Longitude),北纬取90-纬度值(90- Latitude),南纬取90+纬度值(90+Latitude),则经过上述处理过后的两点被计为(MLonA, MLatA)和(MLonB, MLatB)。那么根据三角推导,可以得到计算两点距离的如下公式:

C = sin(MLatA)*sin(MLatB)*cos(MLonA-MLonB) + cos(MLatA)*cos(MLatB)

Distance = R*Arccos(C)*Pi/180

这里,R和Distance单位是相同,如果是采用6371.004千米作为半径,那么Distance就是千米为单位,如果要使用其他单位,比如mile,还需要做单位换算,1千米=0.621371192mile

如果仅对经度作正负的处理,而不对纬度作90-Latitude(假设都是北半球,南半球只有澳洲具有应用意义)的处理,那么公式将是:

C = sin(LatA)*sin(LatB) + cos(LatA)*cos(LatB)*cos(MLonA-MLonB)

Distance = R*Arccos(C)*Pi/180

以上通过简单的三角变换就可以推出。

如果三角函数的输入和输出都采用弧度值,那么公式还可以写作:

C = sin(LatA*Pi/180)*sin(LatB*Pi/180) + cos(LatA*Pi/180)*cos(LatB*Pi/180)*cos((MLonA-MLonB)*Pi/180)

Distance = R*Arccos(C)*Pi/180

也就是:

C = sin(LatA/57.2958)*sin(LatB/57.2958) + cos(LatA/57.2958)*cos(LatB/57.2958)*cos((MLonA-MLonB)/57.2958)

Distance = R*Arccos(C) = 6371.004*Arccos(C) kilometer = 0.621371192*6371.004*Arccos(C) mile = 3958.758349716768*Arccos(C) mile

在实际应用当中,一般是通过一个个体的邮政编码来查找该邮政编码对应的地区中心的经纬度,然 后再根据这些经纬度来计算彼此的距离,从而估算出某些群体之间的大致距离范围(比如酒店旅客的分布范围-各个旅客的邮政编码对应的经纬度和酒店的经纬度所 计算的距离范围-等等),所以,通过邮政编码查询经纬度这样一个数据库是一个很有用的资源。

java代码转换:

public class LocationUtils {
    /**
     * 赤道半径
     */
    private static double EARTH_RADIUS = 6378.137;

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * Description : 通过经纬度获取距离(单位:米)
     * Group :
     *
     * @param origin      出发点
     * @param destination 目的地
     * @return double
     * @author honghh
     * @date 2019/2/13 0013 15:50
     */
    public static double getDistance(String origin, String destination) {
        if (origin == null) {
            log.error("出发点 经纬度不可以为空!");
            return 0;
        }
        if (destination == null) {
            log.error("目的地 经纬度不可以为空!");
            return 0;
        }
        String[] temp = origin.split(",");
        String[] temp2 = destination.split(",");

        double radLat1 = rad(Double.parseDouble(temp[1]));
        double radLat2 = rad(Double.parseDouble(temp2[1]));
        double a = radLat1 - radLat2;
        double b = rad(Double.parseDouble(temp[0])) - rad(Double.parseDouble(temp2[0]));
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        // 保留两位小数
        s = Math.round(s * 100d) / 100d;
        s = s * 1000;
        return s;
    }

    /**
     * Description : 通过经纬度获取距离(单位:米)
     * Group :
     *
     * @param originLon      出发点经度
     * @param originLat      出发点纬度
     * @param destinationLon 目的地经度
     * @param destinationLat 目的地纬度
     * @return double
     * @author honghh
     * @date 2019/2/15 0015 9:14
     */
    public static double getDistance(String originLon, String originLat, String destinationLon, String destinationLat) {
        if (StringUtil.isEmpty(originLon)) {
            log.error("出发点 经度不可以为空!");
            return 0;
        }
        if (StringUtil.isEmpty(originLat)) {
            log.error("出发点 纬度不可以为空!");
            return 0;
        }
        if (StringUtil.isEmpty(destinationLon)) {
            log.error("目的地 经度不可以为空!");
            return 0;
        }
        if (StringUtil.isEmpty(destinationLat)) {
            log.error("目的地 纬度不可以为空!");
            return 0;
        }

        double radLat1 = rad(Double.parseDouble(originLat));
        double radLat2 = rad(Double.parseDouble(destinationLat));
        double a = radLat1 - radLat2;
        double b = rad(Double.parseDouble(originLon)) - rad(Double.parseDouble(destinationLon));
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        // 保留两位小数
        s = Math.round(s * 100d) / 100d;
        s = s * 1000;
        return s;
    }
}

demo:

/**
     * Description : 地理编码 获取详细地址的经纬度
     * Group :
     *
     * @param address     详细地址
     * @param currentCity 市
     * @return java.lang.String
     * @author honghh
     * @date 2019/2/13 0013 15:53
     */
    public static String getLocationGeocodeGeo(String address, String currentCity) {

        Map<String, String> querys = new HashMap<String, String>();
        querys.put("address", address);
        querys.put("city", currentCity);
        querys.put("key", APP_CODE_GAODE); // APP_CODE_GAODE 高德秘钥

        try {
            HttpResponse response = HttpUtils.doGet(QUERY_KEYWD_URL_GAODE_GEOCODE_GEO, "", "GET", null, querys);//QUERY_KEYWD_URL_GAODE_GEOCODE_GEO 高德-地址获取唯一经纬度 : http://restapi.amap.com/v3/geocode/geo
            String resData = EntityUtils.toString(response.getEntity());
            JSONObject json = JSONObject.parseObject(resData);
            if (json == null) {
                logger.error("列表信息获取失败,关键字:" + address + "城市:" + currentCity);
                return null;
            }
            String status = json.getString("status");
            if ("1".equals(status)) {
                JSONArray jsonArray = json.getJSONArray("geocodes");
                if (jsonArray == null || jsonArray.size() == 0) {
                    return null;
                }
                JSONObject object = jsonArray.getJSONObject(0);

                return object.getString("location");
            } else {
                return null;
            }
        } catch (Exception e1) {
            logger.error("调用接口失败!" + e1.getMessage());
            return null;
        }
    }
    
    public static void main(String[] args) {
        String origin = getLocationGeocodeGeo("武林广场", "杭州市");
        String destination = getLocationGeocodeGeo("浙江省杭州市滨江区滨文路470号", "杭州市");
        System.out.println(destination);
        double distance = LocationUtils.getDistance(origin.split(",")[0], origin.split(",")[1], destination.split(",")[0], destination.split(",")[1]);
        //不足一千米用 m 显示
        if (distance < 1000) {
            System.out.println("两地距离: " + new Double(distance).intValue() + "M");
        } else {
            System.out.println("两地距离: " + distance / 1000 + "KM");
        }
        double distance2 = LocationUtils.getDistance(origin, destination);
        System.out.println("两地距离2: " + distance2 / 1000 + "KM");
 
        double dis = LocationUtils.getDistance("120.12026","30.33761","120.124597","30.323823");
        System.out.println("dis  "+dis);
    }

输出:

两地距离: 10.72KM
两地距离2: 10.72KM
dis  1590.0