坐标距离计算
横纬竖经,经线连接南北两极,纬度是与赤道平行的闭合线圈。经度最高180°,纬度最高90°
纬度:北纬为正(+),南纬为负(-)。赤道为0度。
经度:东经为正(+),西经为负(-)。本初子午线(0度)和180度经线为分界线
书写上是先纬后经
东经2°20’14’‘,北纬48°50’11’‘,西经 77°03’56’‘,北纬 38°55’17’’
统一转成度
function du(h,m,s){
if(h < 0){
m = - m
s = -s
}
du= h + m/60 + s/3600
return du
}
l1 = du(-2,20,14)
p1= du(48, 50, 11)
l2 = du(77,03,56)
p2 = du(38,55,17)
低精度Haversine公式,看作球体半径6371km
public class Haversine {
// 地球半径(单位:公里)
private static final int r = 6371;
/**
* 计算两个经纬度坐标点之间的距离(单位:公里)
* @param lat1 点1的纬度
* @param lon1 点1的经度
* @param lat2 点2的纬度
* @param lon2 点2的经度
* @return 两点之间的距离(单位:公里)
*/
public static double jiSuan(double lat1, double lon1, double lat2, double lon2) {
// 将经纬度从度数转换为弧度
double lat1Rad = Math.toRadians(lat1);
double lon1Rad = Math.toRadians(lon1);
double lat2Rad = Math.toRadians(lat2);
double lon2Rad = Math.toRadians(lon2);
// 计算经度差和纬度差
double dLon = lon2Rad - lon1Rad;
double dLat = lat2Rad - lat1Rad;
// 应用Haversine公式
double a = Math.pow(Math.sin(dLat / 2), 2)
+ Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.pow(Math.sin(dLon / 2), 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = r * c;
return distance;
}
}
高精度Vincenty公式,地球当成椭球
public class Vincenty {
// WGS84椭球参数
private static final double A = 6378137.0; // 长半轴 (单位:米)
private static final double F = 1 / 298.257223563; // 扁率
private static final double B = A * (1 - F); // 短半轴
/**
* 计算两个经纬度坐标点之间的距离(单位:米)
* @param lat1 点1的纬度(度数)
* @param lon1 点1的经度(度数)
* @param lat2 点2的纬度(度数)
* @param lon2 点2的经度(度数)
* @return 两点之间的距离(单位:米)
*/
public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
double L = Math.toRadians(lon2 - lon1);
double U1 = Math.atan((1 - F) * Math.tan(Math.toRadians(lat1)));
double U2 = Math.atan((1 - F) * Math.tan(Math.toRadians(lat2)));
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;
double iterLimit = 100;
double sinLambda;
double cosLambda;
double sinSigma;
double cosSigma;
double sigma;
double sinAlpha;
double cosSqAlpha;
double cos2SigmaM;
double C;
double deltaSigma;
do {
sinLambda = Math.sin(lambda);
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; // 两点重合
}
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
sigma = Math.atan2(sinSigma, cosSigma);
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
cosSqAlpha = 1 - sinAlpha * sinAlpha;
cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
if (Double.isNaN(cos2SigmaM)) {
cos2SigmaM = 0; // 防止NaN
}
C = F / 16 * cosSqAlpha * (4 + F * (4 - 3 * cosSqAlpha));
lambdaP = lambda;
lambda = L + (1 - C) * F * sinAlpha *
(sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
iterLimit--;
} while (Math.abs(lambda - lambdaP) > 1e-12 && iterLimit > 0);
if (iterLimit <= 0) {
return Double.NaN; // 迭代未收敛
}
double uSq = cosSqAlpha * (A * A - B * B) / (B * B);
double A2 = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
double B2 = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
deltaSigma = B2 * sinSigma * (cos2SigmaM + B2 / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
B2 / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
double s = B * A2 * (sigma - deltaSigma);
return s;
}
判断一个点是否在多边形区域内-射线法
public class GeoPointInPolygon {
/**
* 判断经纬度点是否在多边形内(射线法)
* @param polygon 多边形顶点列表(按顺序排列,可闭合可不闭合)
* @param testPoint 待测经纬度点
* @return true表示点在多边形内,false表示点在多边形外
*/
public static boolean isPointInPolygon(double[][] polygon, double[] testPoint) {
int n = polygon.length;
boolean inside = false;
for (int i = 0, j = n - 1; i < n; j = i++) {
double[] p1 = polygon[i];
double[] p2 = polygon[j];
// 检查边是否与射线相交
if (((p1[1] > testPoint[1]) != (p2[1] > testPoint[1])) &&
(testPoint[0] < (p2[0] - p1[0]) * (testPoint[1] - p1[1]) / (p2[1] - p1[1]) + p1[0])) {
inside = !inside;
}
}
return inside;
}
public static void main(String[] args) {
// 定义多边形顶点(按顺序排列,可闭合可不闭合)
double[][] polygon = {
{116.3974, 39.9093}, // 顶点1(经度, 纬度)
{116.3974, 39.9151}, // 顶点2
{116.4148, 39.9151}, // 顶点3
{116.4148, 39.9093} // 顶点4(闭合多边形时可不写,但写上也无妨)
};
// 待测经纬度点
double[] pointInside = {116.4061, 39.9121}; // 在多边形内(故宫)
double[] pointOutside = {116.4061, 39.9251}; // 在多边形外
double[] pointOnEdge = {116.3974, 39.9151}; // 在多边形边上
// 判断点是否在多边形内
System.out.println("点 (116.4061, 39.9121) 是否在多边形内: " + isPointInPolygon(polygon, pointInside));
System.out.println("点 (116.4061, 39.9251) 是否在多边形内: " + isPointInPolygon(polygon, pointOutside));
System.out.println("点 (116.3974, 39.9151) 是否在多边形内: " + isPointInPolygon(polygon, pointOnEdge));
}
}