JAVA 计算地球上两点的距离,计算点到线的距离

如题,计算球体上,两个点的距离,以及某个点 到某条线上的最短距离(线也是由球上的两个点组成的),不同于平面,这里不能直接求距离计算,得换算成弧长。


1、确定坐标

首先,我们得获取到 地球上这些点的坐标,那么坐标就有很多种分类,我这里是将他们换算成十进制的经纬度,然后再做计算,所以首先得进行换算,网上有很多现成代码,大家可以自行百度

GIS坐标系转换(EPSG:4326与EPSG:3857相互转换)_是阿洋啊的博客-CSDN博客_4326坐标系转3857


2、 计算点与点的距离


public class Point {
    private double lon;
    private double lat;

    public Point(double lon, double lat) {
        this.lon = lon;
        this.lat = lat;
    }

    public Point() {
    }

    public double getLon() {
        return lon;
    }

    public void setLon(double lon) {
        this.lon = lon;
    }

    public double getLat() {
        return lat;
    }

    public void setLat(double lat) {
        this.lat = lat;
    }
}
	/**
	 * 默认地球半径
	 */
	private static final double EARTH_RADIUS = 6378.137;//赤道半径(单位m)



	/**
	 * 转化为弧度(rad)
	 */
	private static double rad(double d) {
		return d * Math.PI / 180.0;
	}

	/**
	 * 计算 a,b 两个点的距离,单位千米
	 */
	public static double getPPDistance(Point a, Point b) {
		double lon1 = a.getLon();    // lon1 第一点的精度
		double lat1 = a.getLat(); // lat1 第一点的纬度
		double lon2 = b.getLon(); // lon2 第二点的精度
		double lat2 = b.getLat();    // lat2 第二点的纬度

		double radLat1 = rad(lat1);
		double radLat2 = rad(lat2);
		double a1 = radLat1 - radLat2;
		double b2 = rad(lon1) - rad(lon2);
		double s = 2 * Math.asin(Math.sqrt(Math.pow(sin(a1 / 2), 2) + cos(radLat1) * cos(radLat2) * Math.pow(sin(b2 / 2), 2)));
		s = s * EARTH_RADIUS;
		s = Math.round(s * 10000) / 10000;
		return s;
	}

3、计算点到线的距离 

球面上的线是由两个点 a,b组成的,要求点p 到 ab 上的最短距离

首先我们需要求出 三个点之间的各个弧长,然后模拟成平面三角形

根据 海伦公式 知道三边长,可以求出面积,然后 点p对应的边P (也就是ab) 到点 P的高也能求出来了,三角形中,点到边的最短距离,就是高

public static double getPLDistance(Point a, Point b, Point p) {

		// 1、 计算出 三条边长 ab ap bp
		double ab = getPPDistance(a, b);
		double ap = getPPDistance(a, p);
		double bp = getPPDistance(b, p);

		DecimalFormat df = new DecimalFormat("#0.0000"); //保留4位小数
		double S = (ab + ap + bp) / 2;

		// 2、 求面积
		double area = Math.sqrt(S * (S - ab) * (S - ap) * (S - bp)); //求面积

	    // 3、p点到边p的距离=S*2/p
		return Double.parseDouble(df.format(area * 2 / ab));
	}

 4、但是!!有问题

 如果按照上述的方法求,正常情况是没有问题的,但是如果碰到 abp 组成的是一个钝角三角形,且角 P 对应的高在 ab 的延长线上,这样一来 计算出来的高 就并不是 实际上 点P 到 ab  的距离了,例如下图所示

JAVA 计算地球上两点的距离,计算点到线的距离_第1张图片

求 红点 到黄线的最短距离

如果按照 3 中的方法计算,那么计算出来的距离 就是 红点到 红点对应的ab的延长线的橙色点F的距离 pf,那么显然这个距离是不对的,实际距离应该是  bp

还有就是,我们是将两点之间的弧线放到平面当成三角形计算,那么误差在所难免,所以计算出来的 这三条边长 极有可能组不成三角形,这种情况我们就 取  ab 的中点,再计算一次,直到能计算出三角形为止

所以修正后的代码如下


	/**
	 * 计算 p 到点 ab线的 最短距离
	 * 将三个点 组成一个三角形,计算出 三条边长(弧线) 用海伦公式可以算出三角形面积,C点到边c的距离=S*2/c
	 *
	 */
	public static double getPLDistance(Point a, Point b, Point p) {

		// 1、 计算出 三条边长 ab ap bp
		double ab = getPPDistance(a, b);
		double ap = getPPDistance(a, p);
		double bp = getPPDistance(b, p);

		boolean useSideLength = false;

		// 判断是否取边长
		if (isObtuseTriangle(ab, ap, bp) && (ab < ap || ab < bp)) {
			useSideLength = true;
		}

		double s = getMinDistance(a, b, p, useSideLength);


		while (Double.isNaN(s)) {
			b = new Point((a.getLon() + b.getLon()) / 2, (a.getLat() + b.getLat()) / 2);

			// p点 不变,a点不变,b点 变为  ab的中点
			s = getMinDistance(a, b, p, useSideLength);

		}

		return s;
	}

	/**
	 * @param a             航线点a
	 * @param b             航线点b
	 * @param p             船位p
	 */
	public static double getMinDistance(Point a, Point b, Point p, boolean useSideLength) {
		// 1、 计算出 三条边长 ab ap bp
		double ab = getPPDistance(a, b);
		double ap = getPPDistance(a, p);
		double bp = getPPDistance(b, p);


		DecimalFormat df = new DecimalFormat("#0.0000"); //保留4位小数
		double S = (ab + ap + bp) / 2;

		// 2、 求面积
		double area = Math.sqrt(S * (S - ab) * (S - ap) * (S - bp)); //求面积

		// 如果点P对应的边长 无限趋紧于0 ,则取 长度ap bp中最小的
		if (ab == 0) {
			return Double.compare(ap, bp) > 0 ? bp : ap;
		}

		// 如果不是 一个三角形,返回 NaN
		if (Double.isNaN(area)) {
			return area;
		}

		// 如果是钝角三角形,且 最长边 不是 点 P 对应的边 ,则取 与船的两条边长中的最短一条
		if (isObtuseTriangle(ab, ap, bp) && (ab < ap || ab < bp) && useSideLength) {
			return Math.min(bp, ap);
		}

		// 3、p点到边p的距离=S*2/p
		return Double.parseDouble(df.format(area * 2 / ab));
	}

	/**
	 * 判断这三条线组成的 三角形 是不是 钝角三角形
	 */
    private static boolean isObtuseTriangle(double a, double b, double c) {
        // 如果一个三角形的最长边平方>其他两边的平方和,这个三角形是钝角三角形
        double[] doubles = new double[]{a, b, c};
        Arrays.sort(doubles);
        return doubles[2] * doubles[2] > doubles[1] * doubles[1] + doubles[0] * doubles[0];
    }

你可能感兴趣的:(其他,java,java)