我们在实际开发中会获取对应的经纬度,可以使用ES大数据搜索引擎进行计算对应区域的数据,那我们在如何根据两个经纬度获取对应的球面距离,就是在地球上从一个地点到另一个地点的直线距离
工具类如下:
public class GeoUtils {
// 地球半径(单位:米)
private static final double EARTH_RADIUS = 6371000.0;
/**
* 使用Haversine公式计算两点之间的球面距离
*
* @param lat1 latitude 纬度
* @param lon1 longitude 经度
* @param lat2 纬度
* @param lon2 经度
* @return 球面距离
*/
public static double haversineDistance(double lat1, double lon1, double lat2, double lon2) {
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return EARTH_RADIUS * c;
}
/**
* 使用Vincenty公式计算两点之间的球面距离
*
* @param lat1 latitude 纬度
* @param lon1 longitude 经度
* @param lat2 纬度
* @param lon2 经度
* @return 球面距离
*/
public static double vincentyDistance(double lat1, double lon1, double lat2, double lon2) {
double a = EARTH_RADIUS;
double f = 1.0 / 298.257223563; // WGS-84 ellipsoid parameters
double b = a * (1.0 - f);
double lat1Rad = Math.toRadians(lat1);
double lon1Rad = Math.toRadians(lon1);
double lat2Rad = Math.toRadians(lat2);
double lon2Rad = Math.toRadians(lon2);
double L = lon2Rad - lon1Rad;
double U1 = Math.atan((1.0 - f) * Math.tan(lat1Rad));
double U2 = Math.atan((1.0 - f) * Math.tan(lat2Rad));
double sinU1 = Math.sin(U1);
double cosU1 = Math.cos(U1);
double sinU2 = Math.sin(U2);
double cosU2 = Math.cos(U2);
double lambda = L;
double lambdaP;
int iterLimit = 100;
double cosSigma, sinSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM;
do {
double sinLambda = Math.sin(lambda);
double cosLambda = Math.cos(lambda);
sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) +
(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) *
(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
if (sinSigma == 0) {
return 0.0;
}
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
sigma = Math.atan2(sinSigma, cosSigma);
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
cos2SigmaM = cosSigma - 2.0 * sinU1 * sinU2 / cosSqAlpha;
if (Double.isNaN(cos2SigmaM)) {
cos2SigmaM = 0.0;
}
double C = f / 16.0 * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha));
lambdaP = lambda;
lambda = L + (1.0 - C) * f * sinAlpha *
(sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1.0 + 2.0 * cos2SigmaM * cos2SigmaM)));
} while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0);
if (iterLimit == 0) {
return Double.NaN; // Formula failed to converge
}
double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
double A = 1 + uSq / 16384.0 * (4096.0 + uSq * (-768 + uSq * (320 - 175 * uSq)));
double B = uSq / 1024.0 * (256.0 + uSq * (-128.0 + uSq * (74.0 - 47 * uSq)));
double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4.0 * (cosSigma * (-1.0 + 2.0 * cos2SigmaM * cos2SigmaM) -
B / 6.0 * cos2SigmaM * (-3.0 + 4.0 * sinSigma * sinSigma) *
(-3.0 + 4.0 * cos2SigmaM * cos2SigmaM)));
return b * A * (sigma - deltaSigma);
}
/**
* 使用球面三角法计算两点之间的球面距离
*
* @param lat1 latitude 纬度
* @param lon1 longitude 经度
* @param lat2 纬度
* @param lon2 经度
* @return 球面距离
*/
public static double sphericalLawOfCosinesDistance(double lat1, double lon1, double lat2, double lon2) {
double dLon = Math.toRadians(lon2 - lon1);
lat1 = Math.toRadians(lat1);
lat2 = Math.toRadians(lat2);
return Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(dLon)) * EARTH_RADIUS;
}
/**
* 将米转换成公里
*
* @param meters
* @return
*/
public static double metersToKilometers(double meters) {
return meters / 1000.0;
}
}
测试从纽约到伦敦的距离
public static void main(String[] args) {
double newYorkLat = 40.7128; // 纽约的纬度
double newYorkLon = -74.0060; // 纽约的经度
double londonLat = 51.5074; // 伦敦的纬度
double londonLon = -0.1278; // 伦敦的经度
double distance1 = GeoUtils.haversineDistance(newYorkLat, newYorkLon, londonLat, londonLon);
System.out.println("从纽约到伦敦的球面距离:" + distance1 + " 米 ," + metersToKilometers(distance1) + " 公里");
double distance2 = GeoUtils.vincentyDistance(newYorkLat, newYorkLon, londonLat, londonLon);
System.out.println("从纽约到伦敦的球面距离:" + distance2 + " 米 ," + metersToKilometers(distance2) + " 公里");
double distance3 = GeoUtils.sphericalLawOfCosinesDistance(newYorkLat, newYorkLon, londonLat, londonLon);
System.out.println("从纽约到伦敦的球面距离:" + distance3 + " 米 ," + metersToKilometers(distance3) + " 公里");
}
这样子就可以求出地球上两点之间的距离了