返回

精确测量距离,保障位置安全:Java坐标经纬度计算与区域校验全攻略

后端

Java坐标经纬度计算与区域校验指南

在当今互联互通的时代,精确的位置信息已成为现代生活不可或缺的一部分。从导航系统到社交媒体再到物流配送,精准的坐标经纬度计算至关重要。Java作为一门强大的编程语言,提供了丰富的库和算法,让这一过程变得轻松自如。本文将深入探讨Java中坐标经纬度计算和区域校验的方法,提供代码示例和最佳实践。

坐标经纬度计算

Haversine 公式:经典之选

Haversine 公式是一个经典的距离计算公式,它利用球面三角法来计算两点之间的距离。它将地球视为一个球体,并利用三角关系计算两点之间的弧长。

import java.lang.Math;

public class DistanceCalculator {

    private static final double EARTH_RADIUS = 6371.01; // 地球半径,单位:千米

    public static double haversineDistance(double lat1, double lon1, double lat2, double lon2) {
        // 纬度和经度转换为弧度
        lat1 = Math.toRadians(lat1);
        lon1 = Math.toRadians(lon1);
        lat2 = Math.toRadians(lat2);
        lon2 = Math.toRadians(lon2);

        // 计算两点之间的差值
        double dLat = lat2 - lat1;
        double dLon = lon2 - lon1;

        // 使用 Haversine 公式计算距离
        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.cos(lat1) * Math.cos(lat2) *
                        Math.sin(dLon / 2) * Math.sin(dLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        double distance = EARTH_RADIUS * c;

        return distance; // 单位:千米
    }
}

Vincenty 公式:高精度之选

Vincenty 公式比 Haversine 公式更精确,因为它考虑了地球椭圆体的形状。

import java.lang.Math;

public class DistanceCalculator {

    private static final double EARTH_EQUATORIAL_RADIUS = 6378.137; // 地球赤道半径,单位:千米
    private static final double EARTH_POLAR_RADIUS = 6356.752; // 地球极半径,单位:千米

    public static double vincentyDistance(double lat1, double lon1, double lat2, double lon2) {
        // 纬度和经度转换为弧度
        lat1 = Math.toRadians(lat1);
        lon1 = Math.toRadians(lon1);
        lat2 = Math.toRadians(lat2);
        lon2 = Math.toRadians(lon2);

        // 计算经度差和纬度差
        double dLon = lon2 - lon1;
        double dLat = lat2 - lat1;

        // 计算辅助参数
        double a = Math.cos(lat2) * Math.sin(dLon);
        double b = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
        double f = (EARTH_EQUATORIAL_RADIUS - EARTH_POLAR_RADIUS) / EARTH_EQUATORIAL_RADIUS;

        // 迭代计算
        double s = dLat;
        double sigma = 0;
        double t = 0;
        double tau = 0;
        int iteration = 0;
        while (true) {
            iteration++;
            tau = Math.pow(Math.tan(s / 2), 2);
            sigma = 2 * Math.atan2(Math.sqrt(a * a + b * b * tau), Math.sqrt(b * b + (1 - f) * a * a * tau));
            s = dLat - (1 - f) * (Math.sin(s) * Math.cos(sigma) + Math.sin(sigma) * Math.cos(s) * Math.cos(dLon));
            t = 1 / Math.cos(sigma);
            if (Math.abs(s - dLat) < 0.0000001 || iteration >= 20) {
                break;
            }
        }

        // 计算距离
        double distance = EARTH_EQUATORIAL_RADIUS * s * t;

        return distance; // 单位:千米
    }
}

SpheroidalExact 公式:极致之选

SpheroidalExact 公式是 Vincenty 公式的扩展,它考虑了地球椭圆体的扁率。

import java.lang.Math;

public class DistanceCalculator {

    private static final double EARTH_EQUATORIAL_RADIUS = 6378.137; // 地球赤道半径,单位:千米
    private static final double EARTH_POLAR_RADIUS = 6356.752; // 地球极半径,单位:千米
    private static final double EARTH_FLATTENING = 1 / 298.257223563; // 地球扁率

    public static double spheroidalExactDistance(double lat1, double lon1, double lat2, double lon2) {
        // 纬度和经度转换为弧度
        lat1 = Math.toRadians(lat1);
        lon1 = Math.toRadians(lon1);
        lat2 = Math.toRadians(lat2);
        lon2 = Math.toRadians(lon2);

        // 计算经度差和纬度差
        double dLon = lon2 - lon1;
        double dLat = lat2 - lat1;

        // 计算辅助参数
        double a = Math.cos(lat2) * Math.sin(dLon);
        double b = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);

        // 迭代计算
        double s = dLat;
        double sigma = 0;
        double t = 0;
        double tau = 0;
        int iteration = 0;
        while (true) {
            iteration++;
            tau = Math.pow(Math.tan(s / 2), 2);
            sigma = 2 * Math.atan2(Math.sqrt(a * a + b * b * tau), Math.sqrt(b * b + (1 - EARTH_FLATTENING) * a * a * tau));
            s = dLat - (1 - EARTH_FLATTENING) * (Math.sin(s) * Math.cos(sigma) + Math.sin(sigma) * Math.cos(s) * Math.cos(dLon));
            t = 1 / Math.cos(sigma);
            if (Math.abs(s - dLat) < 0.0000001 || iteration >= 20) {
                break;
            }
        }

        // 计算距离
        double distance = EARTH_EQUATORIAL_RADIUS * s * t;

        return distance; // 单位:千米
    }
}

区域校验

圆形区域校验算法

圆形区域校验算法确定一个点是否位于一个圆形区域内。

public class RegionValidator {

    public static boolean isInsideCircle(double lat, double lon, double centerLat, double centerLon, double radius) {
        // 纬度和经度转换为弧度
        lat = Math.toRadians(lat);
        lon = Math.toRadians(lon);
        centerLat = Math.toRadians(centerLat);
        centerLon = Math.toRadians(centerLon);

        // 计算两点之间的距离
        double distance = DistanceCalculator.haversineDistance(lat, lon, centerLat, centerLon);

        // 检查点是否在圆内
        return distance <= radius;
    }
}

多边形区域校验算法

多边形区域校验算法确定一个点是否位于一个多边形区域内。

import java.util.List;

public class RegionValidator {

    public static boolean isInsidePolygon(double lat, double lon, List<double[]> polygon) {
        // 纬度和经度转换为弧度
        lat = Math.toRadians(lat);
        lon = Math.toRadians(lon);

        // 计算多边形边界线的数量
        int n = polygon.size();

        // 沿多边形边界线遍历
        for (int i = 0; i < n; i++) {
            // 获取当前边界线和下一条