栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java中计算两点距离的几种算法

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java中计算两点距离的几种算法

需求

Java计算WGS84坐标系两个点之间的距离,距离可能会较大,

算法 球形模型1

直接采用球形的模型。





    
    @Deprecated
    public static double distance(double lat1, double lat2, double lon1,
                                  double lon2, double el1, double el2) {

        final int R = 6371; // Radius of the earth

        double latDistance = Math.toRadians(lat2 - lat1);
        double lonDistance = Math.toRadians(lon2 - lon1);
        double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
                + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
                * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        double distance = R * c * 1000; // convert to meters

        double height = el1 - el2;

        distance = Math.pow(distance, 2) + Math.pow(height, 2);

        return Math.sqrt(distance);
    }


球形模型2

    
    public static double calculateDistance(Point2D.Double fromPoint, Point2D.Double toPoint) {
        return distanceWithLljk(fromPoint.getX(), fromPoint.getY(), toPoint.getX(), toPoint.getY()) / 1000.0;
    }

    private static double distanceWithLljk(double fromLon, double fromLat, double toLon, double toLat) {
        //地球半径以及塔台和无人机的经纬高赋值
        double r0 = 6371393.0;
        double t1 = fromLon;
        double t2 = fromLat;
        double t3 = 0;
        double a1 = toLon;
        double a2 = toLat;
        double a3 = 0;
        //角度转弧度以及距地心距离计算
        t1 = t1 / 180.0 * Math.PI;
        t2 = t2 / 180.0 * Math.PI;
        t3 = t3 + r0;
        a1 = a1 / 180.0 * Math.PI;
        a2 = a2 / 180.0 * Math.PI;
        a3 = a3 + r0;
        //地球坐标系
        double t11 = t3 * Math.cos(t2) * Math.cos(t1);
        double t12 = t3 * Math.cos(t2) * Math.sin(t1);
        double t13 = t3 * Math.sin(t2);
        double a11 = a3 * Math.cos(a2) * Math.cos(a1);
        double a12 = a3 * Math.cos(a2) * Math.sin(a1);
        double a13 = a3 * Math.sin(a2);
        double x1 = a11 - t11;
        double x2 = a12 - t12;
        double x3 = a13 - t13;

        double r1 = (-Math.sin(t1)) * x1 + Math.cos(t1) * x2;
        double r2 = (-Math.sin(t2)) * Math.cos(t1) * x1 + (-Math.sin(t2) * Math.sin(t1)) * x2 + Math.cos(t2) * x3;
        double r3 = Math.cos(t2) * Math.cos(t1) * x1 + Math.cos(t2) * Math.sin(t1) * x2 + Math.sin(t2) * x3;

        return Math.sqrt(r1 * r1 + r2 * r2 + r3 * r3);
    }
坐标变换
    
    @Deprecated
    public static double calculateDistanceWithUTM(Point2D.Double fromPoint, Point2D.Double toPoint) {
        double[] s = SpatialUtils.wgs84ToUTM(fromPoint.getX(), fromPoint.getY());
        double[] t = SpatialUtils.wgs84ToUTM(toPoint.getX(), toPoint.getY());
        return Math.sqrt(Math.pow((s[0] - t[0]), 2) + Math.pow((s[1] - t[1]), 2)) / 1000.0;
    }
 
    public static double[] wgs84ToUTM(double lon, double lat) {
        double[] xy = new double[2];
        double radPerDegree = MPI / 180;
        int zone = (int) (lon / 6) + 31;
        double a = 6378137;
        double finv = 298.257223563;
        double f = 1 / finv;
        double eDouble = 1 - (1 - f) * (1 - f);
        double koDouble = 0.9996;
        double eoDouble = 500000;
        double noDouble = 0;
        double lcm;
        double lam;
        double latAfter;
        lcm = (zone * 6 - 183) * radPerDegree;
        lam = lon * radPerDegree - lcm;
        latAfter = lat * radPerDegree;
        double rnDouble;
        double h2;
        double t;
        double n;
        rnDouble = a / pow(1 - eDouble * sin(latAfter) * sin(latAfter), 0.5);
        h2 = eDouble * cos(latAfter) * cos(latAfter) / (1 - eDouble);
        t = tan(latAfter);
        n = f / (2 - f);
        double a0;
        double a2;
        double a4;
        double a6;
        double a8;
        double resultS;
        a0 = 1 + n * n / 4 + pow(n, 4) / 64;
        a2 = 3.0 / 2 * (n - pow(n, 3) / 8);
        a4 = 15.0 / 16 * (n * n - pow(n, 4) / 4);
        a6 = 35.0 / 48 * pow(n, 3);
        a8 = 315.0 / 512 * pow(n, 4);
        resultS = a / (1 + n) * (a0 * latAfter - a2 * sin(2 * latAfter) + a4 * sin(4 * latAfter) - a6 * sin(6 * latAfter) + a8 * sin(8 * latAfter));
        double e1;
        double e2;
        double e3;
        double e4;
        double resultE;
        e1 = lam * cos(latAfter);
        e2 = pow(lam, 3) * pow(cos(latAfter), 3) / 6 * (1 - t * t + h2);
        e3 = pow(lam, 5) * pow(cos(latAfter), 5) / 120 * (5 - 18 * t * t + pow(t, 4) + 14 * h2 - 58 * t * t * h2 + 13 * h2 * h2 + 4 * pow(h2, 3) - 64 * t * t * h2 * h2 - 24 * t * t * pow(h2, 3));
        e4 = pow(lam, 7) * pow(cos(latAfter), 7) / 5040 * (61 - 479 * t * t + 179 * pow(t, 4) - pow(t, 6));
        resultE = eoDouble + koDouble * rnDouble * (e1 + e2 + e3 + e4);
        double n1;
        double n2;
        double n3;
        double n4;
        double n5;
        double nResult;
        n1 = resultS / rnDouble;
        n2 = lam * lam / 2 * sin(latAfter) * cos(latAfter);
        n3 = pow(lam, 4) / 24 * sin(latAfter) * pow(cos(latAfter), 3) * (5 - t * t + 9 * h2 + 4 * h2 * h2);
        n4 = pow(lam, 6) / 720 * sin(latAfter) * pow(cos(latAfter), 5) * (61 - 58 * t * t + pow(t, 4) + 270 * h2 - 330 * t * t * h2 + 445 * h2 * h2 + 324 * pow(h2, 3) - 680 * t * t * h2 * h2 + 88 * pow(h2, 4) - 600 * t * t * pow(h2, 3) - 192 * t * t * pow(h2, 4));
        n5 = pow(lam, 8) / 40320 * sin(latAfter) * pow(cos(latAfter), 7) * (1385 - 311 * t * t + 543 * pow(t, 4) - pow(t, 6));
        nResult = noDouble + koDouble * rnDouble * (n1 + n2 + n3 + n4 + n5);
        xy[0] = resultE;
        xy[1] = nResult;

        return xy;
    }

org.gavaghan.geodesy库

https://github.com/mgavaghan/geodesy,这个库也有一个js版本所以前端也可以用。


    
    @Deprecated
    public static double calculateWithSdk(Point2D.Double fromPoint, Point2D.Double toPoint) {
        GlobalCoordinates from = new GlobalCoordinates(fromPoint.getX(), fromPoint.getY());
        GlobalCoordinates to = new GlobalCoordinates(toPoint.getX(), toPoint.getY());
        return new GeodeticCalculator().calculateGeodeticCurve(Ellipsoid.WGS84, from, to).getEllipsoidalDistance() / 1000.0;
    }
public GeodeticCurve calculateGeodeticCurve(Ellipsoid ellipsoid, GlobalCoordinates start, GlobalCoordinates end) {
        double a = ellipsoid.getSemiMajorAxis();
        double b = ellipsoid.getSemiMinorAxis();
        double f = ellipsoid.getFlattening();
        double phi1 = Angle.toRadians(start.getLatitude());
        double lambda1 = Angle.toRadians(start.getLongitude());
        double phi2 = Angle.toRadians(end.getLatitude());
        double lambda2 = Angle.toRadians(end.getLongitude());
        double a2 = a * a;
        double b2 = b * b;
        double a2b2b2 = (a2 - b2) / b2;
        double omega = lambda2 - lambda1;
        double tanphi1 = Math.tan(phi1);
        double tanU1 = (1.0D - f) * tanphi1;
        double U1 = Math.atan(tanU1);
        double sinU1 = Math.sin(U1);
        double cosU1 = Math.cos(U1);
        double tanphi2 = Math.tan(phi2);
        double tanU2 = (1.0D - f) * tanphi2;
        double U2 = Math.atan(tanU2);
        double sinU2 = Math.sin(U2);
        double cosU2 = Math.cos(U2);
        double sinU1sinU2 = sinU1 * sinU2;
        double cosU1sinU2 = cosU1 * sinU2;
        double sinU1cosU2 = sinU1 * cosU2;
        double cosU1cosU2 = cosU1 * cosU2;
        double lambda = omega;
        double A = 0.0D;
        double B = 0.0D;
        double sigma = 0.0D;
        double deltasigma = 0.0D;
        boolean converged = false;

        for(int i = 0; i < 20; ++i) {
            double lambda0 = lambda;
            double sinlambda = Math.sin(lambda);
            double coslambda = Math.cos(lambda);
            double sin2sigma = cosU2 * sinlambda * cosU2 * sinlambda + (cosU1sinU2 - sinU1cosU2 * coslambda) * (cosU1sinU2 - sinU1cosU2 * coslambda);
            double sinsigma = Math.sqrt(sin2sigma);
            double cossigma = sinU1sinU2 + cosU1cosU2 * coslambda;
            sigma = Math.atan2(sinsigma, cossigma);
            double sinalpha = sin2sigma == 0.0D ? 0.0D : cosU1cosU2 * sinlambda / sinsigma;
            double alpha = Math.asin(sinalpha);
            double cosalpha = Math.cos(alpha);
            double cos2alpha = cosalpha * cosalpha;
            double cos2sigmam = cos2alpha == 0.0D ? 0.0D : cossigma - 2.0D * sinU1sinU2 / cos2alpha;
            double u2 = cos2alpha * a2b2b2;
            double cos2sigmam2 = cos2sigmam * cos2sigmam;
            A = 1.0D + u2 / 16384.0D * (4096.0D + u2 * (-768.0D + u2 * (320.0D - 175.0D * u2)));
            B = u2 / 1024.0D * (256.0D + u2 * (-128.0D + u2 * (74.0D - 47.0D * u2)));
            deltasigma = B * sinsigma * (cos2sigmam + B / 4.0D * (cossigma * (-1.0D + 2.0D * cos2sigmam2) - B / 6.0D * cos2sigmam * (-3.0D + 4.0D * sin2sigma) * (-3.0D + 4.0D * cos2sigmam2)));
            double C = f / 16.0D * cos2alpha * (4.0D + f * (4.0D - 3.0D * cos2alpha));
            lambda = omega + (1.0D - C) * f * sinalpha * (sigma + C * sinsigma * (cos2sigmam + C * cossigma * (-1.0D + 2.0D * cos2sigmam2)));
            double change = Math.abs((lambda - lambda0) / lambda);
            if (i > 1 && change < 1.0E-13D) {
                converged = true;
                break;
            }
        }

        double s = b * A * (sigma - deltasigma);
        double alpha1;
        double alpha2;
        if (!converged) {
            if (phi1 > phi2) {
                alpha1 = 180.0D;
                alpha2 = 0.0D;
            } else if (phi1 < phi2) {
                alpha1 = 0.0D;
                alpha2 = 180.0D;
            } else {
                alpha1 = 0.0D / 0.0;
                alpha2 = 0.0D / 0.0;
            }
        } else {
            double radians = Math.atan2(cosU2 * Math.sin(lambda), cosU1sinU2 - sinU1cosU2 * Math.cos(lambda));
            if (radians < 0.0D) {
                radians += 6.283185307179586D;
            }

            alpha1 = Angle.toDegrees(radians);
            radians = Math.atan2(cosU1 * Math.sin(lambda), -sinU1cosU2 + cosU1sinU2 * Math.cos(lambda)) + 3.141592653589793D;
            if (radians < 0.0D) {
                radians += 6.283185307179586D;
            }

            alpha2 = Angle.toDegrees(radians);
        }

        if (alpha1 >= 360.0D) {
            alpha1 -= 360.0D;
        }

        if (alpha2 >= 360.0D) {
            alpha2 -= 360.0D;
        }

        return new GeodeticCurve(s, alpha1, alpha2);
    }

单元测试
package com.zw.se2.platform.utils.spatial;

import org.junit.Test;

import java.awt.geom.Point2D;

import static org.junit.Assert.*;

public class SpatialUtilsTest {

    @Test
    public void calculateDistance() {
        Point2D.Double from=new Point2D.Double(5.998,20.0);
        Point2D.Double to=new Point2D.Double(6.002,19.99999);
        double v = SpatialUtils.calculateDistance(from, to);
        assertEquals(0.417,v,0.01);
    }
    @Test
    public void calculateDistanceWithSame() {
        Point2D.Double from=new Point2D.Double(5.998,20.0);
        double v = SpatialUtils.calculateDistance(from, from);
        assertEquals(v,0,0.0001);
    }
    @Test
    public void calculateDistanceWithLocal() {
        Point2D.Double from=new Point2D.Double(117,39);
        Point2D.Double to=new Point2D.Double(118,39);
        double v = SpatialUtils.calculateDistance(from, to);
        assertEquals(86.36,v,1);
    }
    @Test
    public void calculateDistanceWithTdt() {
        Point2D.Double from=new Point2D.Double(112.67,38.76);
        Point2D.Double to=new Point2D.Double(115.37,37.22);
        double v = SpatialUtils.calculateDistance(from, to);;
        assertEquals(292.3,v,0.1);
    }
}
结论

采用SDK的方式最为准确,与Cesium计算结果高度一致,球形模型2的次之,在跨带后UTM输出结果没法使用,所以建议使用geodesy这个库来实现。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/322842.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号